Android widget's onUpdate() method calls before the ConfigurationActivity and so I receive black screen instead of config screen.
Here is my code.
public class WidgetConfig extends Activity {
int widgetID = AppWidgetManager.INVALID_APPWIDGET_ID;
Intent resultValue;
final String LOG_TAG = "FlashLightLogs";
public final static String WIDGET_TEXT_ENABLED = "widget_text_enabled_";
public final static String WIDGET_HAS_FLASH = "widget_has_flash_";
public final static String WIDGET_PREF = "widget_pref";
private boolean hasFlash;
#Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null) {
widgetID = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
}
if (widgetID == AppWidgetManager.INVALID_APPWIDGET_ID) {
finish();
}
resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetID);
setResult(RESULT_CANCELED, resultValue);
setContentView(R.layout.widget_config);
}
public void onClick(View v) {
CheckBox showLabelCheckbox = (CheckBox) findViewById(R.id.widget_show_text_checkbox);
hasFlash = getApplicationContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
SharedPreferences sp = getSharedPreferences(WIDGET_PREF, MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putBoolean(WIDGET_TEXT_ENABLED + widgetID, showLabelCheckbox.isChecked());
editor.putBoolean(WIDGET_HAS_FLASH + widgetID, hasFlash);
editor.commit();
setResult(RESULT_OK, resultValue);
Log.d(LOG_TAG, "finish config " + widgetID);
finish();
}
}
My manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.pack"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="16" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-feature android:name="android.hardware.camera" />
<application
android:icon="#drawable/icon"
android:label="#string/app_name">
<activity
android:name=".WidgetConfig"
android:icon="#drawable/icon"
android:label="#string/app_name"
android:launchMode="singleTop"
android:exported="true"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
<receiver android:name="com.example.pack.MyProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.intent.action.SCREEN_ON" />
<action android:name="android.intent.action.SCREEN_OFF" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/app_widget" />
</receiver>
<receiver android:name="com.example.pack.MyReceiver">
<intent-filter>
<action android:name="COM_FLASHLIGHT"></action>
</intent-filter>
</receiver>
</application>
</manifest>
In my app_wifget.xml I wrote android:configure
android:configure="com.example.pack.WidgetConfig"
When I try to add widget on the home screen -- I got the black screen and on debug I view that program calls widget's onUpdate.
public class MyProvider extends AppWidgetProvider {
RemoteViews switchButton;
private static boolean hasFlash;
final static String LOG_TAG = "FlashLightLogs";
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
/*
* First check if device is supporting flashlight or not
*/
SharedPreferences sp = context.getSharedPreferences(
WidgetConfig.WIDGET_PREF, Context.MODE_PRIVATE);
if(sp != null) {
for (int id : appWidgetIds) {
updateWidget(context, appWidgetManager, sp, id);
}
}
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
static void updateWidget(Context context, AppWidgetManager appWidgetManager,
SharedPreferences sp, int widgetID) {
Log.d(LOG_TAG, "updateWidget " + widgetID);
hasFlash = sp.getBoolean(WidgetConfig.WIDGET_HAS_FLASH + widgetID, false);
if (!hasFlash) {
return;
}
boolean showLabel = sp.getBoolean(WidgetConfig.WIDGET_TEXT_ENABLED + widgetID, false);
// Widgets do not support ButtonInput directly so, we need to use RemoteViews.
RemoteViews switchButton = new RemoteViews(MainActivity.PACKAGE_NAME, R.layout.widget);
RemoteViews widgetLabel = new RemoteViews(MainActivity.PACKAGE_NAME, R.id.editText);
if(!showLabel) {
widgetLabel.setTextViewText(R.id.editText, "");
}
// Setting FlashlightAppWidgetReceiver as receiver.
Intent intent = new Intent(context, FlashlightAppWidgetReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
//Add listener to button.
switchButton.setOnClickPendingIntent(R.id.widget_button, pendingIntent);
appWidgetManager.updateAppWidget(widgetID, switchButton);
}
}
Related
My widget app is running fine on all android version except 8 Oreo.
I get a W/BroadcastQueue: Background execution not allowed: receiving Intent message.
There is an interesting blog from CommonsWare but I don't fully understand why it applies to my case.
https://commonsware.com/blog/2017/04/11/android-o-implicit-broadcast-ban.html
My case looks pretty simple: I have a widget with a button and I want to change the text's button when it is clicked.
What is the right way to fix this issue?
TestWidget.java
public class TestWidget extends AppWidgetProvider {
private static RemoteViews views;
private static boolean buttonClicked = false;
public static final String ACTION_AUTO_UPDATE = "AUTO_UPDATE";
#Override
public void onReceive(Context context, Intent intent)
{
super.onReceive(context, intent);
if(intent.getAction().equals(ACTION_AUTO_UPDATE))
{
Log.i("TESTWID", "get onReceive");
}
}
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
views = new RemoteViews(context.getPackageName(), R.layout.test_widget);
views.setOnClickPendingIntent(R.id.wid_btn_tst, setButton(context));
appWidgetManager.updateAppWidget(appWidgetId, views);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Log.i("TESTWID", "onupdate ");
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
public static PendingIntent setButton(Context context) {
Intent intent = new Intent();
intent.setAction("TEST");
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public static void pushWidgetUpdate(Context context, RemoteViews remoteViews) {
ComponentName myWidget = new ComponentName(context, TestWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(context);
manager.updateAppWidget(myWidget, remoteViews);
}
}
TestWidgetReceiver.java
public class TestWidgetReceiver extends BroadcastReceiver{
private static boolean isButtonON = false;
#Override
public void onReceive(Context context, Intent intent) {
Log.i("TESTWID", "onReceive "+intent.getAction());
if(intent.getAction().equals("TEST")){
updateWidgetButton(context, 2);
}
}
private void updateWidgetButton(Context context, int index) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.test_widget);
if(index == 2) {
if(isButtonON) {
remoteViews.setTextViewText(R.id.wid_btn_tst, "Test Off");
isButtonON = false;
}
else{
remoteViews.setTextViewText(R.id.wid_btn_tst, "Test On");
isButtonON = true;
}
}
TestWidget.pushWidgetUpdate(context.getApplicationContext(), remoteViews);
}
}
Manifest.xml:
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="Test"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".TestWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<intent-filter>
<action android:name="AUTO_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/test_widget_info" />
</receiver>
<receiver
android:name=".TestWidgetReceiver"
android:label="widgetBroadcastReceiver" >
<intent-filter>
<action android:name="TEST" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/test_widget_info" />
</receiver>
</application>
It's subtle, but it is because of the implicit broadcast being used to trigger your TestWidgetReceiver. It is implicit because it is only specifying the action portion of the Intent. Make the broadcast Intent explicit by specifying the receiver class in the constructor:
public static PendingIntent setButton(Context context) {
Intent intent = new Intent(context, TestWidgetReceiver.class);
intent.setAction("TEST");
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
Things are changed now a days in Android. For the security & battery consumption google introduced so many ways & increased some problem for developers.
You haven't passed the Context of the TestWidgetReceiver.class to you intent.
You can do it like that in java
Intent intent = new Intent(context, TestWidgetReceiver.class);
or in kotlin
val intent = Intent(context, TestWidgetReceiver.class);
You can read more changes here
https://developer.android.com/about/versions/oreo/android-8.0-changes
Regards
I am beginner in working with app widget. Here i have made one app widget of app and in its class set receiver on click of widget icon.Its working when user clicks on it.But the issue is receiver also gets called sometimes automatically.
I am not getting why this is happening.Help will be appreciated.
My app widget's class:
MyWidget.java:
public class MyWidget extends AppWidgetProvider {
static Context cont;
static SharedPreferences preferences;
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
preferences = context.getSharedPreferences("pref", Context.MODE_PRIVATE);
cont = context;
Intent intent2 = new Intent(context, MyReceiver.class);
PendingIntent pendingIntent = PendingIntent.
getBroadcast(context, 0,
intent2, PendingIntent.FLAG_UPDATE_CURRENT);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_widget);
views.setOnClickPendingIntent(R.id.appwidget_text, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
#Override
public void onEnabled(Context context) {
preferences = context.getSharedPreferences("pref", Context.MODE_PRIVATE);
preferences.edit().putBoolean("key", true).commit();
}
#Override
public void onDisabled(Context context) {
preferences = context.getSharedPreferences("pref", Context.MODE_PRIVATE);
preferences.edit().putBoolean("key", false).commit();
}
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
}
}
Manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="*************************">
<application
android:allowBackup="true"
android:icon="#drawable/logo"
android:label="#string/app_name"
android:roundIcon="#drawable/logo"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".Home">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".Myservice" />
<receiver android:name=".MyReceiver"></receiver>
<receiver android:name=".MyWidget">
<intent-filter>
<action android:name="**********************" />
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/my_widget_info" />
</receiver>
</application>
</manifest>
My Midget works perfectly when i drag and drop it on my desktop. However, I want to perform my widget's action from my TestActivity's Interface as well, on button click.
Here is my Widget's receiver-
package com.droideilhan.ultrasimpletaskkiller;
public class USTaskKillerWidget extends AppWidgetProvider {
public static String ACTION_WIDGET_RECEIVER = "ActionReceiverUltraSimpleTaskKillerWidget";
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);
Intent clicSurBtnKillIntent = new Intent(context, USTaskKillerWidget.class);
clicSurBtnKillIntent.setAction(ACTION_WIDGET_RECEIVER);
PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context, 0, clicSurBtnKillIntent, 0);
remoteViews.setOnClickPendingIntent(R.id.btnKill, actionPendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
#Override
public void onReceive(Context context, Intent intent) {
// v1.5 fix that doesn't call onDelete Action
final String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
final int appWidgetId = intent.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
this.onDeleted(context, new int[] { appWidgetId });
}
} else {
// On a cliqué sur le bouton
if (intent.getAction().equals(ACTION_WIDGET_RECEIVER)) {
// Liste des packages à ne pas tuer.
List<String> reservedPackages = new ArrayList<String>();
reservedPackages.add("system");
reservedPackages.add("com.android.launcher2");
reservedPackages.add("com.android.inputmethod.latin");
reservedPackages.add("com.android.phone");
reservedPackages.add("com.android.wallpaper");
reservedPackages.add("com.google.process.gapps");
reservedPackages.add("android.process.acore");
reservedPackages.add("android.process.media");
// On tue tous les processus, sauf ceux de la liste
int compteProcessusTues = 0;
ActivityManager am = (ActivityManager) context.getSystemService(Activity.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> listeProcessus = am.getRunningAppProcesses();
for(RunningAppProcessInfo processus : listeProcessus) {
//Log.d("TKTKTK", "========="+processus.pid+" : "+processus.processName);
String packageName = processus.processName.split(":")[0];
if (!context.getPackageName().equals(packageName) && !reservedPackages.contains(packageName)) {
am.restartPackage(packageName);
compteProcessusTues++;
}
}
// Auto-kill (désactivé dans le cas d'un widget)
//am.restartPackage(context.getPackageName());
}
super.onReceive(context, intent);
}
}
}
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.droideilhan.ultrasimpletaskkiller"
android:versionCode="2"
android:versionName="1.1" >
<uses-sdk android:minSdkVersion="9" />
<application
android:icon="#drawable/icon"
android:label="Google Search" >
<receiver
android:name=".USTaskKillerWidget"
android:label="Google Search" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="com.droideilhan.android.widget.buttons.ButtonWidget.ACTION_WIDGET_RECEIVER" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/ustk_widget_provider" />
</receiver>
<activity
android:name=".MainActivity"
android:label="#string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.RESTART_PACKAGES" >
</uses-permission>
</manifest>
Problem- Widgets's action never gets called, no force closes, no errors.
Here is the button in my TestActivity (well this is what i have tried so far)
public void test(View view) {
Intent intent = new Intent();
intent.setAction("android.appwidget.action.APPWIDGET_UPDATE");
sendBroadcast(intent);
intent.setAction("com.droideilhan.android.widget.buttons.ButtonWidget.ACTION_WIDGET_RECEIVER");
sendBroadcast(intent);
}
I'm starting a VPNService in my activity and I want to start some thread after the service is started. this is how I start the service:
Intent intent = VpnService.prepare(getActivity());
if (intent != null) {
startActivityForResult(intent, 123);
else {
onActivityResult(123, Activity.RESULT_OK, null);
}
And in onActivityResult I start my thread
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 123) {
// Start some thread
}
}
Now I want to be able to do all this from my app's widget. It seems I can only use startActivity from inside a widget, and this is the right way to use VPNService. is there any way I can achieve this from my widget? some callback from startActivity perhaps?
Don't have too much experience with widgets, but would suggest to go that way:
Create dummy activity only for that startActivityForResult() call (please note that I'm using specific theme for that activity #android:style/Theme.NoDisplay);
Start that dummy activity from the widget;
Store result in activity using SharedPreferences and send broadcast for widget;
Below is example project:
AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.alexstarc.testapp"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="19"
android:targetSdkVersion="19"/>
<application
android:label="#string/app_name"
android:icon="#drawable/ic_launcher"
android:allowBackup="true">
<activity
android:name=".MyActivity"
android:label="#string/app_name"
android:theme="#android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<receiver android:name="TestWidgetProvider"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<!-- Custom actions -->
<action android:name="com.alexstarc.testapp.ACTION_BTN" />
<action android:name="com.alexstarc.testapp.ACTION_VPN_RESULT" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/appwidget_info" />
</receiver>
</application>
</manifest>
appwidget_info.xml:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="180dp"
android:minHeight="40dp"
android:updatePeriodMillis="0"
android:initialLayout="#layout/appwidget"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen"
android:previewImage="#drawable/widget_preview"/>
appwidget.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="4dp"
android:background="#drawable/appwidget_bg">
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/launchVPN"
android:src="#android:drawable/ic_media_play"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/resultText"
style="#android:style/TextAppearance.DeviceDefault.Medium.Inverse"
android:layout_toRightOf="#id/launchVPN"
android:text="#string/test_text"
android:gravity="bottom"/>
</RelativeLayout>
MyActivity.java:
public class MyActivity extends Activity {
private static final int VPN_LAUNCH_CODE = 123;
public static String SHARED_PREFS_NAME = "shared_prefs";
public static String RESULT_VPN = "vpn_launch_result";
public static String ACTION_VPN_RESULT = "com.alexstarc.testapp.ACTION_VPN_RESULT";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = VpnService.prepare(MyActivity.this);
if (intent != null) {
startActivityForResult(intent, VPN_LAUNCH_CODE);
} else {
onActivityResult(VPN_LAUNCH_CODE, Activity.RESULT_OK, null);
}
}
#Override
protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 123) {
getSharedPreferences(SHARED_PREFS_NAME, MODE_MULTI_PROCESS).edit().putString(RESULT_VPN, "VPN Result is " + resultCode).commit();
final Intent resultIntent = new Intent(ACTION_VPN_RESULT);
sendBroadcast(resultIntent);
finish();
}
}
}
TestWidgetProvider.java:
public final class TestWidgetProvider extends AppWidgetProvider {
private static final String ACTION_START_BTN = "com.alexstarc.testapp.ACTION_BTN";
// TODO: Use Shared Prefs for this
private static int sWidgetId = 0;
private static boolean sVPNDone = false;
#Override
public void onUpdate(final Context context, final AppWidgetManager appWidgetManager, final int[] appWidgetIds) {
for (int widgetId : appWidgetIds) {
updateWidget(context, appWidgetManager, widgetId);
}
sWidgetId = appWidgetIds[0];
}
/**
* Updates single App Widget
*
* #param context {#link android.content.Context}
* #param appWidgetManager {#link android.appwidget.AppWidgetManager}
* #param widgetId of current widget
*/
private static void updateWidget(final Context context, final AppWidgetManager appWidgetManager, final int widgetId) {
final ArrayList<Integer> widgets = new ArrayList<Integer>();
final Intent btnClickIntent = new Intent(context, TestWidgetProvider.class);
btnClickIntent.setAction(ACTION_START_BTN);
final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, btnClickIntent, 0);
// Get the layout for the App Widget and attach an on-click listener
// to the button
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget);
views.setOnClickPendingIntent(R.id.launchVPN, pendingIntent);
// Update from shared preferences after activity was launched
if (sVPNDone) {
views.setTextViewText(R.id.resultText,
context.getSharedPreferences(MyActivity.SHARED_PREFS_NAME, Context.MODE_PRIVATE).getString(MyActivity.RESULT_VPN, "No result"));
}
appWidgetManager.updateAppWidget(widgetId, views);
}
#Override
public void onReceive(final Context context, final Intent intent) {
final String action = intent.getAction();
if (ACTION_START_BTN.equals(action)) {
// Start listening service
final Intent vpnActivityIntent = new Intent(context, MyActivity.class);
vpnActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(vpnActivityIntent);
} else if (MyActivity.ACTION_VPN_RESULT.equals(action)) {
sVPNDone = true;
updateWidget(context, AppWidgetManager.getInstance(context), sWidgetId);
} else {
super.onReceive(context, intent);
}
}
}
You want to use implicit intents for this kind of thing. You may even package the widget in another application than the service using this scheme if you only make sure to sign the packages with the same key.
AndroidManifest.xml:
<!-- Specify a new kind of permission based on your signature -->
<permission
android:name="com.example.VPN"
android:protectionLevel="signature" />
<!-- This application uses this new permission -->
<uses-permission android:name="com.example.VPN" />
<!-- Let the VPN service use the permission and listen for an implicit intent -->
<service
android:name="com.example.VpnService"
android:exported="true"
android:permission="com.example.VPN" >
<intent-filter>
<action android:name="com.example.intent.action.START_VPN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
VpnService:
public static final String ACTION_START_VPN = "com.example.intent.action.START_VPN";
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if (null != intent && ACTION_START_VPN.equals(intent.getAction()) {
// Do your stuff here
}
}
In your widget:
Intent intent = new Intent(VpnService.ACTION_START_VPN);
context.startService(intent);
I wrote an AppWidget that has a configuration activity(I used the same configuration activity of the app itself)
When adding the Widget to the home screen while on debug mode I pass the widget id(using put extra) to the intent. when clicking on the widget itself(to load the prefs' activity I break at the onCreate method, at the parts where I'm calling intent.getExtras or intent.getIntExtra - I get null.
I wanted to use the following code but couldn;t understand how:
passing-widget-id-to-activity:
The issue was that android does caching with PendingIntents. The solution was to add the FLAG_UPDATE_CURRENT flag which causes it to update the cached PendingIntent.
PendingIntent configPendingIntent = PendingIntent.getActivity(context, REQUEST_CODE_ONE, configIntent, PendingIntent.FLAG_UPDATE_CURRENT);
here is my code:
Manifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test.dryrun" android:versionCode="3"
android:versionName="1.1" android:installLocation="auto">
<uses-sdk android:minSdkVersion="8" />
<application android:icon="#drawable/ic_launcher_test"
android:label="#string/app_name" android:theme="#android:style/Theme.NoTitleBar.Fullscreen"
android:debuggable="true"><!-- different< android:theme="#style/Theme.NoBackground" -->
<!-- Main Activity -->
<activity android:name=".MyActivity"
android:configChanges="orientation"> <!--android:screenOrientation="portrait" -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Preferences -->
<activity android:name=".Preferences.EditPreferences"
android:configChanges="orientation">
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</activity>
<!-- Widgets -->
<!-- Widget-->
<receiver android:name=".Widget.testWidget" android:label="#string/app_widget_">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<!--action
android:name="com.test.dryrun.Widget.testWidget.PREFENCES_WIDGET_CONFIGURE" /-->
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/test_widget__provider" />
</receiver>
<service android:name=".Widget.testWidget$WidgetService" />
<uses-permission android:name="android.permission.BIND_REMOTEVIEWS"></uses-permission>
</application>
</manifest>
appwidget_provider xml
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="146dip"
android:minHeight="146dip"
android:updatePeriodMillis="0"
android:initialLayout="#layout/test_widget_"
/>
Widget Class
public class testWidget extends AppWidgetProvider {
public static String PREFENCES_WIDGET_CONFIGURE = "ActionConfigureWidget";
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
Intent svcIntent = new Intent(context, WidgetService.class);
context.startService(svcIntent);
}
#Override
public void onReceive(Context context, Intent intent)
{
RemoteViews remoteViews = new RemoteViews(
context.getPackageName(), R.layout.test_widget);
// v1.5 fix that doesn't call onDelete Action
final String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action))
{
final int appWidgetId = intent.getExtras().getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID)
{
this.onDeleted(context, new int[] { appWidgetId });
}
}
else
{
super.onReceive(context, intent);
}
}
//public void updateWidget()
/**
* #param context
* #param remoteViews
*/
public static void updateWidget(Context context, RemoteViews remoteViews)
{
String Prefix = context.getString(R.string._prefix);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
String ToShow = prefs.getString(context.getString(
R.string.Widget_string),
context.getString(R.string.default_string));
String pkgName = context.getPackageName();
int resID = context.getResources().getIdentifier(Prefix + ToShow, "drawable", pkgName);
WidgetController widgetController = WidgetController.getInstance();
widgetController.setRemoteViewImageViewSource(remoteViews, R.id.WidgetImage, resID);
}
public static class WidgetService extends Service
{
#Override
public void onStart(Intent intent, int startId)
{
super.onStart(intent, startId);
// Update the widget
RemoteViews remoteView = buildRemoteView(this);
// Push update to homescreen
WidgetController.getInstance().pushUpdate(
remoteView,
getApplicationContext(),
testWidget.class);
// No more updates so stop the service and free resources
stopSelf();
}
public RemoteViews buildRemoteView(Context context)
{
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.test_widget_);
Intent runConfigtest = new Intent(context, EditPreferences.class);
runConfigtest.setAction(testWidget.PREFENCES_WIDGET_CONFIGURE);
//old code-what you get in all the widget examples
PendingIntent runtestPendingIntent = PendingIntent.getActivity(context, 0, runConfigtest, 0);
//new code - this is how you should write it
PendingIntent runtestPendingIntent = PendingIntent.getActivity(context, 0, runConfigtest, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.WidgetImage, runtestPendingIntent);
updateWidget(context, remoteViews);
return remoteViews;
}
#Override
public void onConfigurationChanged(Configuration newConfig)
{
int oldOrientation = this.getResources().getConfiguration().orientation;
if(newConfig.orientation != oldOrientation)
{
// Update the widget
RemoteViews remoteView = buildRemoteView(this);
// Push update to homescreen
WidgetController.getInstance().pushUpdate(
remoteView,
getApplicationContext(),
testWidget.class);
}
}
#Override
public IBinder onBind(Intent arg0)
{
// TODO Auto-generated method stub
return null;
}
}
}
Prefences class
public class EditPreferences extends PreferenceActivity implements OnSharedPreferenceChangeListener
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
Intent intent = getIntent();
m_extras = intent.getExtras();
mAppWidgetId = intent.getIntExtra("widget_id", defaultVal);
}
private Bundle m_extras;
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
{
if(key.equals(getString(R.string.rlvntString)))
{
Context ctx = getApplicationContext();
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(ctx);
setResult(RESULT_CANCELED);
if (m_extras != null)
{
mAppWidgetId = m_extras.getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
RemoteViews views = new RemoteViews(ctx.getPackageName(),
R.layout.test_widget);
appWidgetManager.updateAppWidget(mAppWidgetId, views);
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
}
else
{
RemoteViews views = new RemoteViews(ctx.getPackageName(),
R.layout.test_widget);
appWidgetManager.updateAppWidget(mAppWidgetId, views);
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
}
}
}
I needed to change the next thing:
//old code-what you get in all the widget examples
PendingIntent runtestPendingIntent = PendingIntent.getActivity(context, 0, runConfigtest, 0);
//new code - this is how you should write it
PendingIntent runtestPendingIntent = PendingIntent.getActivity(context, 0, runConfigtest, PendingIntent.FLAG_UPDATE_CURRENT);
now it works