So, for few last hours I was trying to make a proper Android widget, so far without success.
Issue is that my Intents aren't getting updated.
So basically, I have a configuration activity, when the widget is created, it works as it's supposed to be.
I also wanted to make a settings button, which will allow user to open the configuration activity and change some settings.
In particular I want to allow user to open some pdf files through the widget, when it's being set during right after creation of widget it's fine, but when it's being changed through the settings button only UI of widget (TextView showing which file is going to be opened), but the intent to open the file is not changed.
I was debugging code, file is properly chosen (that's why TextView get's updated properly) intent is supposed to be properly set, but it still opens the file chosen at beginning.
That's the part of my code:
Intent intent = getIntent(); //intent to get the widget ID
Bundle extras = intent.getExtras();
if (extras != null) {
mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
RemoteViews views = new RemoteViews(this.getPackageName(), R.layout.wi_widget_layout); //widget layout
views.setTextViewText(R.id.button_plan, fileCleanName); //setting text of the TextView showing pdf's name, works just fine
File pdfFile = StorageModel.getFileForName(fileName, this); //method to get the file, while debugging, proper file is taken
Intent openFile = StorageModel.openFile(pdfFile, this); // Intent method, returns intent to open pdf application for the given file
openFile.putExtra("Random", Math.random() * 1000);
//I've read somewhere Android is caching intents, so I tried it but didn't help
PendingIntent pendingFile = PendingIntent.getActivity(this, 0, openFile, 0);
views.setOnClickPendingIntent(R.id.button_plan, pdfFile);
Intent settings = new Intent(this, WidgetConfigure.class).setAction(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
//remaking intent for the settings button
settings.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,mAppWidgetId);
PendingIntent settingsPending = PendingIntent.getActivity(this, 0, settings, 0);
views.setOnClickPendingIntent(R.id.button_settings, settingsPending);
appWidgetManager.updateAppWidget(mAppWidgetId, views);
setResult(RESULT_OK);
finish();
}
If anyone happens to come to similar issue, apparently this part of code was ok, the issue was in the method that was returning the intent to open file, even though it was very simple.
The intent to open my pdf file was made by calling
createChooser
method on my final intent, after return just normal intent everything is working as it should. I guess Android doesn't like to give a choice :D
Related
I want to open the launcher's widget picker (for example, the one we get when we long press on home screen) from my Activity. What I want to achieve is, I want to take the user to my widget so that there are more chances that he will consider adding it.
Programmatically adding the widget to home screen will be the best case. But, because that is not possible, I want to go as closer as possible to make user add the widget.
I tried the following but that only opens a dialog (not the launcher's) with all the widgets and by selecting one nothing happens.
Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0);
startActivityForResult(pickIntent, 1234);
How to make this work? Any suggestions on this scenario are much appreciated.
It's possible to pin widget to homescreen (official doc) on API 26+:
AppWidgetManager mAppWidgetManager =
context.getSystemService(AppWidgetManager.class);
AppWidgetProviderInfo myWidgetProviderInfo = new AppWidgetProviderInfo();
ComponentName myProvider = myWidgetProviderInfo.provider;
if (mAppWidgetManager.isRequestPinAppWidgetSupported()) {
Intent pinnedWidgetCallbackIntent = new Intent( ... );
PendingIntent successCallback = PendingIntent.createBroadcast(context, 0,pinnedWidgetCallbackIntent);
mAppWidgetManager.requestPinAppWidget(myProvider, null,
successCallback.getIntentSender());
}
I have a problem (2 problems to be exact) with launching intents from a Notification.
My current situation is that I have a family of apk that all can use a remote service launched by the first of them. The service creates an Notification and when clicked the opens back the application that launched the service. This works ok.
I wanted to improve that so when there is more than one application from the family installed, instead of just going to the apk that launched the service an chooser Intent would appear and the user would be able to choose the apk to come back to.
I managed to do this.
ArrayList<String> myApps = Lists.newArrayList(
"com.myapp1",
"com.myapp2",
"com.myapp3",
"com.myapp4"
);
List<Intent> targetedIntents = new ArrayList<Intent>();
Intent baseIntent = new Intent(Intent.ACTION_MAIN, null);
baseIntent.addCategory("android.intent.category.LAUNCHER");
final PackageManager packageManager = getApplicationContext().getPackageManager();
List<ResolveInfo> list = packageManager.queryIntentActivities(baseIntent, 0);
for (ResolveInfo resolveInfo : list) {
String packageName = resolveInfo.activityInfo.packageName;
if (packageName != null && myApps.contains(packageName)) {
Intent targetedIntent = new Intent();
targetedIntent.setPackage(packageName);
targetedIntent.setClassName(packageName, resolveInfo.activityInfo.name);
targetedIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
targetedIntents.add(targetedIntent);
}
}
Intent intent = Intent.createChooser(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=whatever")), "Select app to return to");
intent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedIntents.toArray(new Parcelable[]{}));
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, intent.getFlags());
notification.setLatestEventInfo(this, notificationTitle, notificationMessage, contentIntent);
This works mostly as it should. The chooser appears and the selected option launches the desired apk.
But I came across 2 problems:
I. When I create the intent chooser with only the intents that interest me, the chooser is empty with the message "no application can perform this action"
intent = Intent.createChooser(targetedIntents.get(0), "Select app to return to");
intent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedIntents.toArray(new Parcelable[]{}));
But if I put an existing apk there first (such as google play) everything works and my options show along with the google play option.
intent = Intent.createChooser(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=whatever")), "Select app to return to");
intent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedIntents.toArray(new Parcelable[]{}));
This is something I can live with, but it would be better if onlye the proper apks were there.
II. When I click the apk from the list, instead of coming back from the background (if the apk was already running there) it restarts it. I have the proper flags set.
Intent targetedIntent = new Intent();
targetedIntent.setPackage(packageName);
targetedIntent.setClassName(packageName, resolveInfo.activityInfo.name);
targetedIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
Before using the chooser I launched only one intent and it resummed the background apk normally.
intent = new Intent(this, MainActivity.class);
intent.setAction("android.intent.action.MAIN");
intent.addCategory("android.intent.category.LAUNCHER");
Not sure what I am doing wrong here that the apk restarts insted of resuming.
For the first problem (chooser issue), the reason you are getting the error "no application can perform this action" is because the Intent you are passing to getChooser() doesn't have an ACTION in it that Android can use to search for applications that can handle the Intent. getChooser will use the ACTION, CATEGORY and DATA in the Intent to search for suitable applications. It will then use package and component names to filter this list. In your case, because you've only provided the package and component names (but haven't specified ACTION), Android can't find any suitable applications. I'm not sure there's a way around this, since you want to build a list with different packages. You may just need to create your own chooser dialog (which is probably the correct thing to do here anyway, as you don't really get any advantages using the Android chooser because you've already decided what the list should contain).
For the second problem (if application is in background, it gets restarted) you need to use the following flags:
targetedIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
Specifying Intent.FLAG_ACTIVITY_NEW_TASK when launching the root activity of a task will bring an already existing task to the foreground.
NOTE: If you want to just bring the existing task to the foreground (in whatever state it happens to be), then just use Intent.FLAG_ACTIVITY_NEW_TASK and remove the other 2 flags.
Within my Main Activity that loads a Webview, depending on where it was launched from determines the url to load.
Bundle extras = getIntent().getExtras();
if (extras != null) {
String theurl = getIntent().getExtras().getString("url");
webView.loadUrl(theurl);
} else {
webView.loadUrl("http://mysite.com");
}
If the application is a fresh instance and loaded from the home screen it loads the default url.
If the application is not running and i click a notification that was sent through google cloud messaging the application loads a custom url.
This functions as expected.
My problem lies when the application is already running in the background, when i click a notification it will bring the app to the front but wont load the new url, it stays at the default.
I'm not too sure with the Flags, i did have this problem before and it was a certain combination of flags that fixed it.
Intent notificationIntent = new Intent(context, MainActivity.class);
notificationIntent.putExtra("url", url);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );
int requestID = (int) System.currentTimeMillis();
PendingIntent intent = PendingIntent.getActivity(context, requestID, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT);
Help would be greatly appreciated.
EDIT
If i add the flag PendingIntent.FLAG_CANCEL_CURRENT this almost solves the issue, although it reloads the whole main activity (Splash screen gets called), i want it to reuse the existing webview.
This is the flag you're looking for: FLAG_ACTIVITY_CLEAR_TASK
If set in an Intent passed to Context.startActivity(), this flag will
cause any existing task that would be associated with the activity to
be cleared before the activity is started. That is, the activity
becomes the new root of an otherwise empty task, and any old
activities are finished. This can only be used in conjunction with
FLAG_ACTIVITY_NEW_TASK.
EDIT:
If you place your webview code in onResume and then don't use any flags, it should work fine.
I have made a working clock widget app in android but would like it so that the user could change just the clock face by clicking on it and going to a settings page. Is there a simple way of doing this? I am fairly new to programming for android.
You could try to use an Intent to start the settings Activity like this:
Intent intent = new Intent(context, SettingsActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
remoteViews.setOnClickPendingIntent(R.id.widget, pendingIntent);
If you use this code in the onUpdate method of the AppWidgetProvider you wrote, it should do the trick for you.
Ofcourse, SettingsActivity.class and R.id.widget need to be replaced by whatever you have called them.
If you would also like to have the settings Activity pop up automatically when first adding the widget to the home screen, you could incorporate this in you app widget provider information XML file:
android:configure="com.example.yourapplication.SettingsActivity"
In order for this to work well, you should take a look at the Android Developer pages.
I have a widget application, which I have made clickable to reach to another application. However, I am stuck on how to get to a specific fragment in my application. My code is making me open the application, but I want to open a specific page. For that, I tried to put the package of the class I want to be displayed but I got a null pointer exception on the category launcher line. Is it possible? This is my code so far. TIA
Intent in = new Intent(Intent.ACTION_MAIN);
PackageManager manager = context.getPackageManager();
in = manager.getLaunchIntentForPackage("com.playup.android");
in.addCategory(Intent.CATEGORY_LAUNCHER);
PendingIntent pendingIntent = PendingIntent.getActivity(context,0 /* no requestCode */, in, 0 /* no flags */);