How to access Appication objects from Sync Adapter - android

I am working on an IM application with open fire server. I'm implementing Sync Adapter for managing the contacts.
From my sync adapter's onPerformSync() if I access the connection object which I kept in my Application class it returns null.
What am I doing wrong? How should I do that?
My Application class
public class MyApp extends Application {
private Connection connection;
private static MyApp instance;
public static MyApp getInstance() {
return instance;
}
public static void setInstance(MyApp instance) {
MyApp.instance = instance;
}
#Override
public void onCreate() {
super.onCreate();
instance = new MyApp();
}
public Connection getAuthenticatedConnection() throws NullPointerException {
if (instance.connection != null
&& instance.connection.isAuthenticated()) {
return instance.connection;
} else {
// Calling service which will create connection and update the object
Intent intent = new Intent(IM_Service_IntentMessaging.ACTION);
intent = new Intent(getContext(), IM_Service_IntentMessaging.class);
Bundle b = new Bundle();
b.putInt("key", IM_Service_IntentMessaging.KEY_CONNECTION);
intent.putExtras(b);
this.context.startService(intent);
throw new NullPointerException();
}
//service calls this method to update the object
public void setConnection(Connection connection) {
instance.connection = connection;
}
}
And my onPerformSync method is as follows...
#Override
public void onPerformSync(Account account, Bundle bundle, String authority,
ContentProviderClient provider, SyncResult syncResult) {
try {
this.connection = MyApp.getInstance()
.getAuthenticatedConnection();
this.roster = this.connection.getRoster();
} catch (NullPointerException e) {
//this line executes always
Log.e(TAG, "connection null...ending....");
return;
}
this.mAccount = account;
Log.d(TAG, "...onPerformSync...");
getAllContacts();
}
When I tried with creating break point in Application's getAuthenticatedConnection() method it didn't get triggered but the service IM_Service_IntentMessaging which i called from there to create connection is working.

I found the solution to my problem is i should remove the android:process=":sync" from the sync adapter's manifest file declaration. Because of that it treats like different process.

Related

Activity to Android Library communication options (IPC)

Lets say I have a SDK in form of Android Library (aar) that offers some basic media processing (it has its own UI as a single activity). Currently, any client Android app, when invoking my SDK sends required data via Bundle.
Now, for various reasons some extra info for the data being sent may be required after my SDK is invoked so I would need a two-way communication with the caller app.
In short, from within the SDK I need to be able to check if the client app has implemented some interface so that SDK can use it to communicate with the client app (the client may choose not to provide the implementation in which case the SDK will fallback to internal, the default implementation..).
Anyway, the way I've done it initialy, is as following:
Within SDK I have exposed the data provider interface:
public interface ISDKDataProvider {
void getMeSomething(Params param, Callback callback);
SomeData getMeSomethingBlocking(Params param);
}
a Local binder interface that should return an instance of the implemented interface:
public interface LocalBinder {
ISDKDataProvider getService();
}
Then, on the client side, an application using the SDK, must provide a service that does the job and implements those interfaces:
public class SDKDataProviderService extends Service implements ISDKDataProvider {
private final IBinder mBinder = new MyBinder();
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
#Override
public void getMeSomething(Params param, Callback callback) {
// ... do something on another thread
// once done, invoke callback and return result to the SDK
}
#Override
public SomeData getMeSomethingBlocking(Params param);{
// do something..
// return SomeData
}
public class MyBinder extends Binder implements LocalBinder {
#Override
public ISDKDataProvider getService() {
return ISDKDataProvider.this;
}
}
}
Additionally, when invoking SDK, the clinet app passes the ComponentName via bundle options:
sdkInvokationOptions.put("DATA_PROVIDER_EXTRAS", new ComponentName(getPackageName(), SDKDataProviderService.class.getName()));
..from the SDK, I then check whether the service exists and whether we can bind to it:
final ComponentName componentName = // get passed componentname "DATA_PROVIDER_EXTRAS"
if (componentName != null) {
final Intent serviceIntent = new Intent(componentName.getClassName());
serviceIntent.setComponent(componentName);
bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE);
}
where mConnection is:
private boolean mBound;
private ISDKDataProvider mService;
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
final LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
};
This seem to work ok and it looks clean but my question is there a better way\practice to accomplish the same type of a communication?
Your API should be simple, for example a static class/singleton:
MyAPI.start()
MyAPI.stop()
MyAPI.sendMessgage(mgs,callback)
MyAPI.setCallback(callback)
About the service, I think you should decide who is in charge of it.
If its the user - leave him the implementation, just give the API.
If you always want your API to run as a service, implement it yourself and inside the singleton handle the messaging (you can do so with intents, for example).
I used this architecture for image processing service too :)
My API wrapping class looked like:
public class MyAPI {
public static final String TAG = "MyAPI";
public MyAPI() {
}
public static MyAPI.Result startMyAPI(ScanParams scanParams) {
try {
Log.d("MyAPI", "in startMyAPI");
if (scanParams.ctx == null || scanParams.appID == null || scanParams.api_key == null) {
Log.d("MyAPI", "missing parameters");
return MyAPI.Result.FAILED;
}
if (scanParams.userID == null) {
scanParams.userID = "no_user";
}
if (scanParams.minBatteryThreshold == null) {
scanParams.minBatteryThreshold = Consts.DEFAULT_BATTERY_THRESHOLD;
}
if (scanParams.minCpuThreshold == null) {
scanParams.minCpuThreshold = Consts.DEFAULT_CPU_THRESHOLD;
}
if (!DeviceUtils.checkBatteryLevel(scanParams.ctx, (float)scanParams.minBatteryThreshold)) {
ReportUtils.error("low battery");
return MyAPI.Result.FAILED;
}
if (MyAPIUtils.isRunning(scanParams.ctx)) {
return MyAPI.Result.FAILED;
}
Intent intent = new Intent(scanParams.ctx, MyAPIService.class);
ServiceParams serviceParams = new ServiceParams(scanParams.appID, scanParams.api_key, scanParams.userID, scanParams.minBatteryThreshold, scanParams.minCpuThreshold);
intent.putExtra("SERVICE_PARAMS", serviceParams);
scanParams.ctx.startService(intent);
} catch (Exception var3) {
var3.printStackTrace();
}
return MyAPI.Result.SUCCESS;
}
public static void getBestCampaignPrediction(Context ctx, String apiKey, String appID, String creativeID, AppInterface appInterface) {
try {
String deviceID = DeviceUtils.getDeviceID(ctx);
GetBestCampaignTask getBestCampaignTask = new GetBestCampaignTask(ctx, apiKey, deviceID, appID, creativeID, appInterface);
getBestCampaignTask.execute(new Void[0]);
} catch (Exception var7) {
var7.printStackTrace();
}
}
public static boolean sendAdEvent(Context ctx, String apiKey, Event event) {
boolean res = false;
try {
boolean isValid = Utils.getIsValid(ctx);
if (isValid) {
Long timeStamp = System.currentTimeMillis();
event.setTimeStamp(BigDecimal.valueOf(timeStamp));
event.setDeviceID(DeviceUtils.getDeviceID(ctx));
(new SendEventTask(ctx, apiKey, event)).execute(new Void[0]);
}
} catch (Exception var6) {
var6.printStackTrace();
}
return res;
}
public static enum PredictionLevel {
MAIN_CATEGORY,
SUB_CATEGORY,
ATTRIBUTE;
private PredictionLevel() {
}
}
public static enum Result {
SUCCESS,
FAILED,
LOW_BATTERY,
LOW_CPU,
NOT_AUTHENTICATED;
private Result() {
}
}
}
You can see that startMyAPI actually starts a service and getBestCampaignPrediction runs an async task that communicates with the service behind the scenes and returns its result to appInterface callback. This way the user get a very simple API

How to get Socket Id in Socket.io on Android?

I want to get the id of my socket running in Android. Everything is create normally and is able to send data back and forth but I need to get the Androids socket id for usage throughout the entire application. I have tried to find a solution but either they were outdated or does not work. Below is my latest attempt at solving this issue. With this attempt it errors saying that there are no elements in args[0]. I have also tried mSocket.id(); but that returns nothing.
public class GameSocket {
private Socket mSocket = null;
private static GameSocket mInstance;
public synchronized static GameSocket getInstance() {
if (mInstance == null) {
mInstance = new GameSocket();
}
return mInstance;
}
public void initialize() {
mSocket = IO.socket(URI.create(SERVER_URI));
mSocket.off();
mSocket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
Log.d(GameSocket.class.getSimpleName(), "Event connection...");
mSocketId = args[0].toString();
Log.d("CONNECT ID", mSocketId);
}
})
.on(Socket.EVENT_ERROR, new Emitter.Listener() {
#Override
public void call(Object... args) {
if (args != null) {
Log.e(GameSocket.class.getSimpleName(), "Socket Event Error: " + args[0].toString());
}
}
});
}
}

Google Analytics deserialization with Gson is returning LinkedTreeMap

I'm trying to pass an object containing Analytics Reporting data in an Intent via a broadcast. The problem is the deserialization which returns a LinkedTreeMap instead of the original serialized object, causing a crash with ClassCastException.
I tried to follow quite all answers found here on SO, from using TypeToken to modify ProGuard rules and nothing worked.
I thought to implement Parcelable interface but the problem is that I have an inner private AsyncTask class where the data is collected and pushed into the intent which will be sent via broadcast.
Here is the code of the helper where data is serialized:
public class AnalyticsHelper
{
...
private class GoogleBatchTask extends AsyncTask<GetReportsRequest,Void,GetReportsResponse>
{
#Override
protected GetReportsResponse doInBackground(#NonNull GetReportsRequest... reports)
{
GetReportsResponse response = null;
try {
if (m_reports == null)
return null;
response = m_reports.reports().batchGet(reports[0]).execute();
} catch (IOException e) {
Console.log(e);
}
return response;
}
#Override
protected void onPostExecute(GetReportsResponse response)
{
Intent intent = new Intent();
intent.setAction("com.keyone.contactpackapp.ANALYTICS_DATA");
intent.putExtra("response", new Gson().toJson(response));
Context context = PackConfig.instance().context();
if (context == null)
return;
context.sendBroadcast(intent);
}
}
}
AnalyticsFragment.java, where the deserialization happens:
public class AnalyticsFragment extends Fragment
{
#Override
public void onResume()
{
super.onResume();
// Listen to custom intent with data
IntentFilter filter = new IntentFilter("com.keyone.contactpackapp.ANALYTICS_DATA");
m_receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent)
{
// Get data from intent and pass it to the right fragment
String szJson = intent.getStringExtra("response");
//m_response = new Gson().fromJson(szJson, GetReportsResponse.class);
Type listType = new TypeToken<GetReportsResponse>(){}.getType();
m_response = new Gson().fromJson(szJson, listType);
Fragment fragment = m_activity.currentFragment();
fragment.updateData();
}
};
if (m_activity != null)
m_activity.registerReceiver(m_receiver, filter);
}
}
There was no way to deserialize object in a correct way using Gson neither using Java Serializable interface or Android Parcelable interface due to the nature of the objects to serialize.
So I opted to call an instance of the recipient class and pass object data through a method in it

Salesforce Rest API with android - NullPointerException # AsyncRequestCallback

I'm trying to get the Salesforce REST API working with Android and new to android programming, followed the sample code to connect with SFDC http://wiki.developerforce.com/page/Getting_Started_with_the_Mobile_SDK_for_Android#Authentication
I'm trying to get a few records from SFDC and display them in the android app, looks like when the Async Call is made at "client.sendAsync(sfRequest, new AsyncRequestCallback()" - NullPointerException is thrown.
I did see a couple of similar issues online, but didn't help me. Hoping if some one would point me in the right direction to troubleshoot this. Thanks much.
public class GetAccountsActivity extends Activity {
private PasscodeManager passcodeManager;
private String soql;
private String apiVersion;
private RestClient client;
private TextView resultText;
private RestRequest sfRequest;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Get Api Version
apiVersion = getString(R.string.api_version);
//Create Query
soql = "select id, name from Account limit 10";
// Setup view
setContentView(R.layout.get_accounts_activity);
((TextView) findViewById(R.id.Acc_Title)).setText(apiVersion);
// Passcode manager
passcodeManager = ForceApp.APP.getPasscodeManager();
}
#Override
public void onResume() {
super.onResume();
//Get SFClient
// Login options
String accountType = ForceApp.APP.getAccountType();
LoginOptions loginOptions = new LoginOptions(
null, // login host is chosen by user through the server picker
ForceApp.APP.getPasscodeHash(),
getString(R.string.oauth_callback_url),
getString(R.string.oauth_client_id),
new String[] {"api"});
new ClientManager(this, accountType, loginOptions).getRestClient(this, new RestClientCallback() {
#Override
public void authenticatedRestClient(RestClient client) {
if (client == null) {
ForceApp.APP.logout(GetAccountsActivity.this);
return;
}
GetAccountsActivity.this.client = client;
}
});
//Get Rest Object to query
try {
sfRequest = RestRequest.getRequestForQuery(apiVersion, soql);
//Use SF Rest Client to send the request
client.sendAsync(sfRequest, new AsyncRequestCallback(){
#Override
public void onSuccess(RestRequest request, RestResponse response){
//Check responses and display results
// EventsObservable.get().notifyEvent(EventType.RenditionComplete);
}//end onSuccess
#Override
public void onError(Exception exception) {
//printException(exception);
EventsObservable.get().notifyEvent(EventType.RenditionComplete);
}//End Exception for Async Method
});
}catch (UnsupportedEncodingException e) {
//printHeader("Could Send Query request");
//printException(e);
return;
}
}
}
enter code here
You are calling client.sendAsync from onResume() but client is not set until the authenticatedRestClient callback is called, you need to move your sendAsync call into the authenticatedRestClient callback.

how to pass data between service and it's application in the right way?

i'm a newbie in android. In my app i create a many-to-many chat, and need to update from server a list of Messages. In order to do so, i created a service that updates every second from the server.
My problem is that i don't know how to pass data back to the application. I know that I should do it using intent and broadcast receiver, but in that I stuck with Bundle object that i have to serialize in order to pass it to the app, and it does not make sense to me, since this operation is not that efficient.
For now i'm using the ref to my application (i think it's not that good but don't know why), and after every update from server in the service i activate the application function, and updates it's fields directly. Moreover i think maybe my code will do some good for beginners as well :)
public class UpdateChatService extends Service {
private static final long DELAY_FOR_CHAT_TASK = 0;
private static final long PERIOD_FOR_CHAT_TASK = 1;
private static final TimeUnit TIME_UNIT_CHAT_TASK = TimeUnit.SECONDS;
//private Task retryTask; TODO: check this out
private ScheduledExecutorService scheduler;
private boolean timerRunning = false;
private long RETRY_TIME = 200000;
private long START_TIME = 5000;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
scheduleChatUpdate();
}
private void scheduleChatUpdate() {
BiggerGameApp app = (BiggerGameApp) getApplication();
this.scheduler = Executors.newScheduledThreadPool(3);
this.scheduler.scheduleAtFixedRate(new UpdateChatTask(app),
DELAY_FOR_CHAT_TASK, PERIOD_FOR_CHAT_TASK,
TIME_UNIT_CHAT_TASK);
timerRunning = true;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!timerRunning) {
scheduleChatUpdate();
}
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
if (scheduler != null) {
scheduler.shutdown();
}
timerRunning = false;
}
}
Here is the code of the asynchronous task the runs in the service.
Please tell me what i'm doing wrong, and how should pass data from the service to the application.
public void run() {
try {
if (this.app.getLastMsgFromServer() == null) {
this.app.setLastMsgFromServer(new Message(new Player(DEFAULT_EMAIL), "", -1));
this.app.getLastMsgFromServer().setMessageId(-1);
}
Gson gson = new GsonBuilder()
.registerTypeAdapter(DateTime.class, new DateTimeTypeConverter())
.create();
ServerHandler serverHandler = new ServerHandler();
String jsonString = gson.toJson(this.app.getLastMsgFromServer());
// Sending player to servlet in server
String resultString = serverHandler.getResultFromServlet(jsonString, "GetListOfMessages");
if (resultString.contains("Error")) {
return;
}
// Parsing answer
JSONObject json = new JSONObject(resultString);
Status status = null;
String statusString = json.getString("status");
if (statusString == null || statusString.length() == 0)
return;
status = Status.valueOf(statusString);
if (Status.SUCCESS.equals(status)) {
ArrayList<Message> tempChat = null;
JSONArray jsonList = json.getJSONArray("data");
MyJsonParser jsonParser = new MyJsonParser();
tempChat = jsonParser.getListOfMessagesFromJson(jsonList.toString());
if (tempChat != null && tempChat.size() != 0) {
// After getting the chat from the server, it saves the last msg
// For next syncing with the server
this.app.setLastMsgFromServer(tempChat.get(LAST_MSG_INDEX));
tempChat.addAll(this.app.getChat());
if (tempChat.size() > SIZE_OF_USER_CHAT) {
tempChat = (ArrayList<Message>) tempChat.subList(0, SIZE_OF_USER_CHAT - 1);
}
this.app.setChat(tempChat);
this.app.updateViews(null);
}
}
return;
Is the Service local only (I'm going to assume "yes")?
Communication with a local-only service can be done by passing an instance of android.os.Binder back, as shown below:
public class UpdateChatService extends Service {
public static final class UpdateChat extends Binder {
UpdateChatService mInstance;
UpdateChat(UpdateChatService instance) {
mInstance = instance;
}
public static UpdateChat asUpdateChat(IBinder binder) {
if (binder instanceof UpdateChat) {
return (UpdateChat) binder;
}
return null;
}
public String pollMessage() {
// Takes a message from the list or returns null
// if the list is empty.
return mInstance.mMessages.poll();
}
public void registerDataSetObserver(DataSetObserver observer) {
mInstance.mObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mInstance.mObservable.unregisterObserver(observer);
}
}
private ScheduledExecutorService mScheduler;
private LinkedList<String> mMessages;
private DataSetObservable mObservable;
#Override
public IBinder onBind(Intent intent) {
return new UpdateChat(this);
}
#Override
public void onCreate() {
super.onCreate();
mObservable = new DataSetObservable();
mMessages = new LinkedList<String>();
mScheduler = Executors.newScheduledThreadPool(3);
mScheduler.scheduleAtFixedRate(new UpdateChatTask(), 0, 1, TimeUnit.SECONDS);
}
#Override
public void onDestroy() {
super.onDestroy();
mScheduler.shutdownNow();
mObservable.notifyInvalidated();
}
class UpdateChatTask implements Runnable {
int mN = 0;
public void run() {
// This example uses a list to keep all received messages, your requirements may vary.
mMessages.add("Message #" + (++mN));
mObservable.notifyChanged();
}
}
}
This example could be used to feed an Activity (in this case a ListActivity) like this:
public class ChattrActivity extends ListActivity implements ServiceConnection {
LinkedList<String> mMessages;
ArrayAdapter<String> mAdapter;
UpdateChat mUpdateChat;
DataSetObserver mObserver;
Runnable mNotify;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMessages = new LinkedList<String>();
mNotify = new Runnable() {
public void run() {
mAdapter.notifyDataSetChanged();
}
};
mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mMessages);
getListView().setAdapter(mAdapter);
// Bind to the Service if you do not need it to persist when this Activity
// dies - otherwise you must call #startService(..) before!
bindService(new Intent(this, UpdateChatService.class), this, BIND_AUTO_CREATE);
}
/**
* #see android.app.ListActivity#onDestroy()
*/
#Override
protected void onDestroy() {
super.onDestroy();
if (mUpdateChat != null) {
mUpdateChat.unregisterDataSetObserver(mObserver);
unbindService(this);
}
}
public void onServiceConnected(ComponentName name, IBinder service) {
mUpdateChat = UpdateChat.asUpdateChat(service);
mObserver = new DataSetObserver() {
#Override
public void onChanged() {
String message;
while ((message = mUpdateChat.pollMessage()) != null) {
mMessages.add(message);
}
runOnUiThread(mNotify);
}
#Override
public void onInvalidated() {
// Service was killed - restart or handle this error somehow.
}
};
// We use a DataSetObserver to notify us when a message has been "received".
mUpdateChat.registerDataSetObserver(mObserver);
}
public void onServiceDisconnected(ComponentName name) {
mUpdateChat = null;
}
}
If you need to communicate across processes you should look into implementing an AIDL interface - but for "local" versions this pattern works just fine & doesn't involve abusing the global Application instance.
You can use a static memory shared between your service and rest of application (activities). If you do not plan to expose this service to external apps, then sharing static memory is better than serializing/deserializing data via bundles.
Bundles based approach is encouraged for components that are to be exposed to outside world. A typical app usually has just the primary activity exposed in app manifest file.
If your don't pulibc your service , the static memory and the callback function can do.
If not , you can send broadcast.

Categories

Resources