I wrote a collection widget to show calllogs in home screen.
So there may have 2 receivers exist: one is widget provider the other is call log content observer. Im not sure whether content observer is a receiver or not.
CallLogAppWidgetProvider is declared in manifest.xml and I need to store some data in this receiver object (for using later in observer).
CallLogContentObserver is constructed and registered when WidgetProvider triggered. Observer is triggered when calls are made.
The problem is, when I first place my widget, widgetprovide is constructed, triggered and onUpdate is called, I can see CallLogAppWidgetProvider.mContext and CallLogAppWidgetProvider.mWidgetIds is set in app's main thread(tid=8148,pid=8148).
but when calls are made, CallLogContentObserver.onChange is called in the same thread(tid=8148,pid=8148). Inside onChange, mContext has the value that set previously but mWidgetIds is null, so mywidget could not reflect calllog's changes.
According to this post: Save data in Broadcast receiver , my widgetprovider may be destroyed when it returned, so it could explain why mWidgetIds is null, but conflict with mContext stored old value. And that post did not explain why we need persist our data in receivers.
If widgetprovider is destroyed, then when observer triggered how did android start to run observer's onChange? Is it the same mechanism like closures in lua? Why mContext can store a value?
If widgetprovider is not destroyed, then why mWidgetIds is null when observer runs? I dont know exactly how java passes primitive array, according to this post: Is an array a primitive type or an object (or something else entirely)? , arrays are runtime classes created by jvm, so mWidgetIds is a strong reference, the integer array mWidgetIds points to can not be gc after provider returned, because at least mWidgetIds points to it. What happend when provider returned?
Could you please tell me what on earth happend behind thses scenes? Any information will be precitated, thank you.
code is below:
public class CallLogAppWidgetProvider extends AppWidgetProvider {
private static final String TAG = "CallLogAppWidgetProvider";
private ContentObserver mContentObserver = null;
private Handler mHandler = new Handler();
private Context mContext = null; //data to store
private int[] mWidgetIds; //data to store
protected class CallLogContentObserver extends ContentObserver {
public CallLogContentObserver(Handler handler) {
super(handler);
// TODO Auto-generated constructor stub
}
#Override
public void onChange(boolean selfChange) {
AppWidgetManager.getInstance(mContext).notifyAppWidgetViewDataChanged(mWidgetIds, R.id.calllog_list);
}
}
#Override
public void onEnabled(Context context) {
mContext = context; //store data when first place in home screen
mContentObserver = new CallLogContentObserver(mHandler);
context.getContentResolver().registerContentObserver(CallLog.CONTENT_URI, true, mContentObserver);
}
#Override
public void onDisabled(Context context) {
context.getContentResolver().unregisterContentObserver(mContentObserver);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
mWidgetIds = appWidgetIds; //store data when first place in home screen
for (int i = 0; i < appWidgetIds.length; i++) {
Intent intent = new Intent(context, CallLogRemoteViewsService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.calllog_appwidget);
rv.setRemoteAdapter(R.id.calllog_list, intent);
appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
}
Related
I am currently making an app that containing a widget.
When placing the app a WidgetConfigure Activity opens where you can select an object out of a recycleViewer.
The object (Kitten) implements the parcelable and the recyleviewer is filled by an adapter class.
When the eventListner of that recyleviewer is triggered, the selected object must be passed to the widget since I need properties from that object in my widget.
I'm currently completely stuck and confused how I can pass an object to the widget.
How can I pass an object to the widget?
Here is my code that will run in the eventListner in
LongEventConfigureActivity:
private void makeWidget(Event myObj){
// How I normally passing data to an activity/fragment
//Intent i = new Intent();
//Bundle b = new Bundle();
//b.putParcelable(CONST_PARCELKEY, myObj);
//i.putExtras(b);
//i.setClass(this, ObjectDetailActivity.class);
//startActivity(i);
// Example of how to pass data to the widget
final Context context = LongEventWidgetConfigureActivity.this;
// When the button is clicked, store the string locally
String widgetText = txtWidgetText.getText().toString();
saveTitlePref(context, mAppWidgetId, widgetText);
// It is the responsibility of the configuration activity to update the app widget
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
LongEventWidget.updateAppWidget(context, appWidgetManager, mAppWidgetId);
// Make sure we pass back the original appWidgetId
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
}
#Override
public void onItemClick(Event e) {
makeWidget(e);
}
Also I need to find a way when the user tapping this widget, a certain activity of my app will open that will receive that object.
Summery of the problem (I don't have the privilege to post an image yet)
I have an Activity with a Recycler view handling cards. I have a SwipeRefreshLayout so when I swipe it updates the RecyclerView with new content. So far, so good.
Howerver, I want to update the RecyclerView every X seconds so if, for instance, I leave the activity with the recylcerview opened and I forgot to swipe it, it would as well update by itself.
To do that, I thought something like this:
( I ommitted necessary code ).
My main Activity which contains the recyclerview schedules a Job like this:
private void scheduleJob() {
ComponentName serviceName = new ComponentName(this, MyJobService.class);
JobInfo jobInfo = new JobInfo.Builder(JOB_ID, serviceName)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresDeviceIdle(false)
.setRequiresCharging(false)
.setPeriodic(3000)
//.setOverrideDeadline(400) // Remove comment for faster testing.
.build();
JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
int result = scheduler.schedule(jobInfo);
if (result == JobScheduler.RESULT_SUCCESS) {
Toast.makeText(this,"Start", Toast.LENGTH_LONG).show();
} } }
Inside MyJobService I through a Broadcast to my Activity, to let it know it has to update the content of the RecylcerView.
The responsable of receiving the broadcast is an inner class like this:
public static class MyReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
DataSource dataSource = new DataSource(context);
items = dataSource.getItems(); // can't do this due to outter class variable
adapter.refresh(ítems); // can't do this due to outter class variable
Toast.makeText(context,"event",Toast.LENGTH_SHORT).show();
}
}
Problem is on my main Activity I hold two private variable called items (which is the ArrayList ) and adapter ( which is the adapter of the recylcerview) I pass items to the adapter to update the recyclerView content.However, as I am in a static inner class I can’t access outter class variables.
Whats the correct way to do something like this? I think I am messing too much and I guess there must be an easiest and more straight forward way to accomplish what I want.
Thank you very much
Well, turns out I was complicating things too much. It can be done with a simple Handler like this:
// Create the Handler object (on the main thread by default)
Handler handler = new Handler();
// Define the code block to be executed
private Runnable runnableCode = new Runnable() {
#Override
public void run() {
// Do something here on the main thread
Toast.makeText(getApplicationContext(),"Update",Toast.LENGTH_SHORT).show();
ItemsDS itemsDS = new ItempsDS(getApplicationContext());
items = itemsDS.getItems();
adapter.clear();
adapter.addAll(items);
handler.postDelayed(runnableCode, 1000);
}
};
There are Two ways to do this thing:
First is as mentioned by #akshayBhat, you can do it by removing static keyword from BroadcastReceiver class:
ArrayList<String> items;
Adapter adapter;
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
DataSource dataSource = new DataSource(context);
items = dataSource.getItems(); // can't do this due to outter class variable
adapter.refresh(ítems); // can't do this due to outter class variable
Toast.makeText(context, "event", Toast.LENGTH_SHORT).show();
}
}
Second is to pass the reference of current activity state to the BroadCast Receiver and access ClassVariables there using that refernce;
ArrayList<String> items;
Adapter adapter;
public static class MyReceiver extends BroadcastReceiver {
AboutUs mContext;
#Override
public void onReceive(Context context, Intent intent) {
mContext = (AboutUs)context;
DataSource dataSource = new DataSource(context);
mContext.items = dataSource.getItems(); // can't do this due to outter class variable
mContext.adapter.refresh(ítems); // can't do this due to outter class variable
Toast.makeText(context, "event", Toast.LENGTH_SHORT).show();
}
}
Hi I'm trying to run a code which has one activity and a BroadcastReceiver which runs when new message comes and yes it's run clearly but I have a problem with BroadcastReceiver object !
It's part of MainActivity CLASS :
public class MainActivity extends FragmentActivity {
private IncomingSms checkAndDo; //=> OBJECT
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
checkAndDo= new IncomingSms();
checkAndDo.setProgramState(210); // program state is a variable
checkAndDo.getProgramState(this); // Toast output : " >>>210 "
....
But problem starts when a new message comes and onReceived() called ! IncommingSms class :
public class IncomingSms extends BroadcastReceiver {
private int programState=110; // Which state we are ? 110=> white / 111=> off / 210=>black ...
public void onReceive(Context context, Intent intent) {
final Bundle bundle = intent.getExtras();
this.getProgramState(context);
// This method called again but toast output is : ">>>110"
// which is initial value !?
AND ....
}
public void setProgramState(int status) {
this.programState=status;
}
public void getProgramState( Context context) {
Toast.makeText(context, ">>>"+this.programState , Toast.LENGTH_LONG).show();
}
question : I'm not sure why it happens but onReceive() uses only the initial value which is so bad . ANY Idea?
this.getProgramState(context);
// This method called again but toast output is : ">>>110"
// which is initial value !?
You met that problem because you registered your BroadcastReceiver in the manifest. So Android will create a new BroadcastReceiver and pass the sms broadcast to it.
If you would like to get the state you set in your program, you must register your BroadcastReceiver in your Activity.
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
registerReceiver(checkAndDo, filter);
using Intent data instead bundle
Intent(ContextHolder.applicationContext!!, NotificationDeleteReceiver::class.java).setData(conversation.conversationId.toUri())
My problem is, I am transferring two arrays between activities. with an return from the one to another Activity. There I am getting these two Activities by the constructor( I think ). Is this correct? Why I am getting the error: no empty constructor?
Here is my class where the Activites are coming from:
public PlanOutputActivity fetchallRoutes(String startStop, String endStop, String time) {
.
.
.
return new PlanOutputActivity(convertArray(),routenArray);
Here is my Activity where i wanna get these two Arrays:
public class PlanOutputActivity extends Activity {
Intent intent;
Object[][] routenArray;
String[][] itemsArray;
DatabaseHelperActivity mdbH;
public int RESULT_OK = 123;
public int REQUEST_OK = 456;
public PlanOutputActivity(String[][] itemsArray, String[][] routenArray){
setContentView(R.layout.planoutputlayout);
this.routenArray = routenArray;
this.itemsArray = itemsArray;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated constructor stub
super.onCreate(savedInstanceState);
}
public void getRoute() {
intent = getIntent();
mdbH = new DatabaseHelperActivity(this);
mdbH.fetchallRoutes(intent.getStringExtra("StartHaltestelle"),intent.getStringExtra("ZielHaltestelle"),intent.getStringExtra("Zeit"));
ListView lvList = (ListView)findViewById(R.id.ListOutput);
ArrayAdapter<DefineRouteActivity> adapter = new RouteAdapterActivity(this, route);
lvList.setAdapter(adapter);
lvList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
int gKennung = view.getId();
Intent intent = new Intent(getApplicationContext(),DetailOutputActivity.class);
intent.putExtra("Kennung",gKennung);
intent.putExtra("routenArray",routenArray);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
});
}
You can't create an instance of an Android Activity using new - it simply doesn't work.
The only ways to create an Activity are either when it is launched from the Apps launcher or if you call startActivity(...) (or one of the other methods such as startActivityForResult(...) etc).
Because of that you shouldn't ever create constructors for an Activity and you should never create public static fields or methods with the intention of accessing data or calling methods in an Activity from any other application class.
If you want to pass data from one Activity to another do it using the extras in an Intent or simply persist the data in SharedPreferences or a database. Alternatively create a 'helper' class to hold the data - using the Singleton pattern is quite common for this.
I have a widget which needs to do some operations in another thread. It has a method which processes some data and shows the result in a TextView on the widget.
To implement this, I need to call my method in a separate thread. Then I need to include some code to the end of that method to show the results on the widget (something like textView1.setText("my results");. This has to be done in a UI thread. I want to use the method runOnUiThread. But it exists only for an activity. Is there an analog of this method for widget class? What can I do to use that method in my widget?
Here is my code:
public class MyWidgetProvider extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds)
{
//...
//here I need to launch a method toBeLaunchedInASeparateThread in a separate thread
}
public void toBeLaunchedInASeparateThread()
{
// ... many operations
int result = 0;
//here I need to launch the following line in the UI thread to follow Android guidelines
setTextToTextView(context, "My result is " + result);
}
public static void setTextToTextView(Context context, String text)
{
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_sample);
views.setTextViewText(R.id.widgetTextViewCard, text);
ComponentName myWidget = new ComponentName(context, MyWidgetProvider.class);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(manager.getAppWidgetIds(myWidget), views);
}
}
you can use an Handler, to connect with the UI Thread and post on its queue. Handler.post(Runnable). Here the documentation
blackbelt's answer is great, however you can also use textView1.post(runnable) and methods like that. This way you don't have to create a Handler instance, and the code will run on UI thread. AFAIK both solution will do the exactly same things.
For RemoteViews:
Handler mHandler = new Handler();//this must be ran on the UI thread.
....
mHandler.post(new Runnable(){
public void run() {
toBeLaunchedInASeparateThread();
}
});