How do I organise my code? I have tried to put everything in a different package but then my notifications with GCM doesn't work anymore. if i put everything in the same root package no problem.
this are my two tests, left gives problems on gcm right doesn't:
Is there any way to easy organise your code like I did in the right one? or do all the classes, activities, services needs to be in the same package?
EDIT 1 = SOLUTION
You need to create your own class that extends GCMBroadcastReceiver like this:
import android.content.Context;
import com.google.android.gcm.GCMBroadcastReceiver;
public class GCMReceiver extends GCMBroadcastReceiver{
#Override
protected String getGCMIntentServiceClassName(Context context) {
return "yourAppPackage.yourNewAddedPackage.GCMIntentService";
}
}
Then you need to change the default receiver in your manifest to the package where this class is located. for example:
<receiver android:name="yourPackage.PackageOfTheGCMReceiver.GCMReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="be.vanlooverenkoen.piautomation" />
</intent-filter>
</receiver>
If you want to organize your code, you have patterns options like: MVC, There is a great debate about whether Android uses default MVC.
But I see your picture in your structure you want a recommendation for .java files.
I give the following recommendation that I have seen in different files, books and github repositories (The packages are in alphabetical order):
activities
adapters
authenticator
data
data.migrations
fragments
helpers
interfaces
models
preferences
sync
You need to create your own class that extends GCMBroadcastReceiver like this:
import android.content.Context;
import com.google.android.gcm.GCMBroadcastReceiver;
public class GCMReceiver extends GCMBroadcastReceiver{
#Override
protected String getGCMIntentServiceClassName(Context context) {
return "yourAppPackage.yourNewAddedPackage.GCMIntentService";
}
}
Then you need to change the default receiver in your manifest to the package where this class is located. for example:
<receiver android:name="yourPackage.PackageOfTheGCMReceiver.GCMReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="be.vanlooverenkoen.piautomation" />
</intent-filter>
</receiver>
Related
I'm implementing a simple App that can receive 'share's from the YouTube App and simply sends it to a server.
I'm able to pass the Intent to a ShareActivity I've implemented, but every time I share a video from YouTube, it opens the ShareActivity.
Since the user doesn't need to interact with the App while sharing, I'd like to remove this phase entirely. That is, I'd like to receive the Intent from Youtube and process it in the background without any hassle to the user.
I'm thinking of creating an IntentService that can handle such incoming Intents, but I'm not sure how to proceed. (Is this the right way to achieve this? If so, from where should I start the IntentService?, Will it be able to receive Intents if the user kills my app?)
I can't seem to find any resources on the Internet regarding this. Any help is appreciated.
Edit: Okay, This is a minimal, not-working example. I hope this can help you guys guide me better. Cause I'm stuck.
Android Manifest File
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.blumonkey.versatyl">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
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>
<service
android:name=".ShareService"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:host="www.youtube.com" android:mimeType="text/*" />
</intent-filter>
</service>
</application>
MainActivity.java
package com.blumonkey.versatyl;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent msgIntent = new Intent(this, ShareService.class);
startService(msgIntent);
}
}
ShareService.java
package com.blumonkey.versatyl;
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
/**
* An {#link IntentService} subclass for handling asynchronous task requests in
* a service on a separate handler thread.
* <p>
* TODO: Customize class - update intent actions and extra parameters.
*/
public class ShareService extends IntentService {
public ShareService() {
super("ShareService");
}
#Override
protected void onHandleIntent(Intent intent) {
Log.d("Msg:", "Got Intent!");
}
}
The only message that I see in the Log is when the MainActivity sends the Intent to the service to start it.
IntentService would be fine, or even a regular Service if you will need to use resources for long period of time - you just need to add an IntentFilter with the proper filter options to the service in the manifest.
The Android docs explain it for both Activities and Services:
https://developer.android.com/guide/components/intents-filters.html
Sometimes an Intent is designed to be handled by an Activity so just be cautious about that.
And this is the correct way to "wake up" your app if a user "kills" it. An Intent that your app is designed to handle will receive it. However, sometimes users can set preferred apps to handled certain Intents, in which case you do not have control over what happens.
Following the Google developer instructions on implementing Firebase in my app, I notice that android lint complains.
The idea is that we have to implement two services which inherit from Firebase services:
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService { ... }
public class MyFirebaseMessagingService extends FirebaseMessagingService { ... }
and then register those services in the manifest. But, it's not quite perfect. In particular, these two recommended AndroidManifest.xml service entries do not contain any special permissions:
<service android:name=".MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
<service android:name=".MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
and so the linter says:
Exported services (services which either set exported=true or contain an intent-filter and do not specify exported=false) should define a permission that an entity must have in order to launch the service or bind to it. Without this, any application can use this service.
Should I just add this attribute to each service tag and be done with it
tools:ignore="ExportedService"
or is there a better approach in this situation? I mean, is it safe to expose these particular Firebase derived services like this?
You ask: ...is it safe to expose these particular Firebase derived services like this? It is if you trust the comments in the manifest files for these services.
In Android Studio, open your app's AndroidManifest.xml file. At the bottom of the window, select the tab for Merged Manifest. Scroll to find the entry for FirebaseMessagingService. Double-click on the line that contains the service name. The manifest file for the service should open and you will see this:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.google.firebase.messaging">
<uses-sdk android:minSdkVersion="14"/>
<application>
<!-- FirebaseMessagingService performs security checks at runtime,
no need for explicit permissions despite exported="true" -->
<service android:name="com.google.firebase.messaging.FirebaseMessagingService" android:exported="true">
<intent-filter android:priority="-500">
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
</application>
</manifest>
Note the comment: FirebaseMessagingService performs security checks at runtime, no need for explicit permissions despite exported="true"
You can do the same for FirebaseInstanceIdService and see the same comment.
If you trust the comments (I do), you can safely ignore the lint warnings or disable the checks.
<service android:name=".java.MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
Based on the official code sample, it's safe to set exported=false
I would like to put the GCMIntentService in a directory other than my package root.
The GCM documentation states that
By default, it must be named .GCMIntentService, unless the
application uses a custom BroadcastReceiver that redefines its name.
My question is - how do I create this "custom BroadcastReceiver" thay talk about?
Try this -- Rename or change package of GCMIntentService class
The basic docs have you add the following to your manifest:
<receiver android:name="com.google.android.gcm.GCMBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="my_app_package" />
</intent-filter>
</receiver>
This points to a GCM-supplied BroadcastReceiver that will route events to your .GCMIntentService. If you wish to have your service reside in some other package, you will need to supply your own BroadcastReceiver. This may be as simple as creating one that subclasses GCMBroadcastReceiver and overrides getGCMIntentServiceClassName() to return the fully-qualified class name of the service to use.
I am using ACTION_MY_PACKAGE_REPLACED to receive when my app is updated or resinstalled.
My problem is that the event is never triggered (I tried Eclipse and real device).
This is what I do:
Manifest:
<receiver android:name=".MyEventReceiver" >
<intent-filter android:priority="1000" >
<action android:name="android.intent.action.ACTION_MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
Code:
public class MyEventReceiver extends BroadcastReceiver
{
#Override public void onReceive(Context context, Intent intent)
{
if ("android.intent.action.ACTION_MY_PACKAGE_REPLACED".equals(intent.getAction()))
{ //Restart services
}
}
}
This code is simple, in real one I used other events like BOOT_COMPLETED and others, and they work but ACTION_MY_PACKAGE_REPLACED.
Thanks.
for some reason, a google developer (named "Dianne Hackborn") thinks it is possible to register to the PACKAGE_REPLACED intent of the current app alone (read archived version here, original link here).
however, i can't find any way of doing it correctly, so i've made a compromise: it will use the newest API when available.
Sadly, I can't find out why it can't be debugged, but it does work (you can write to the log if you wish).
here's the code:
manifest:
<receiver
android:name=".OnUpgradeBroadcastReceiver"
android:enabled="#bool/is_at_most_api_11" >
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
<receiver
android:name=".OnUpgradeBroadcastReceiver"
android:enabled="#bool/is_at_least_api_12" >
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
res/values/version_checks.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="is_at_least_api_12" type="bool">false</item>
<item name="is_at_most_api_11" type="bool">true</item>
</resources>
res/values-v12/version_checks.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="is_at_least_api_12" type="bool">true</item>
<item name="is_at_most_api_11" type="bool">false</item>
</resources>
OnUpgradeBroadcastReceiver.java
public class OnUpgradeBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
if (VERSION.SDK_INT <= VERSION_CODES.HONEYCOMB
&& !context.getPackageName().equals(intent.getData().getSchemeSpecificPart())) {
android.util.Log.d("AppLog", "other apps were upgraded");
return;
}
android.util.Log.d("AppLog", "current app was upgraded");
EDIT: In today's Android versions, when you should set minSdk to be at least 14, you don't need this, and indeed you should just use MY_PACKAGE_REPLACED and that's it. No need for the booleans etc...
The accepted answer doesn't work any more with Android Studio 1.0+ because of manifest merge issues, as seen here. Totally based on android developer's answer, I fixed the issue with the following implementation:
AndroidManifest.xml:
<receiver android:name=".UpdateReceiver$LegacyUpdateReceiver" android:enabled="#bool/shouldUseActionPackageReplaced" >
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
<receiver android:name=".UpdateReceiver" android:enabled="#bool/shouldUseActionMyPackageReplaced" >
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
/res/values/resources.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="shouldUseActionPackageReplaced">true</bool>
<bool name="shouldUseActionMyPackageReplaced">false</bool>
</resources>
/res/values-v12/resources.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="shouldUseActionPackageReplaced">false</bool>
<bool name="shouldUseActionMyPackageReplaced">true</bool>
</resources>
UpdateReceiver.java:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class UpdateReceiver extends BroadcastReceiver
{
public static class LegacyUpdateReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
if (intent != null && intent.getData() != null && context.getPackageName().equals(intent.getData().getSchemeSpecificPart()))
{
onUpdate(context);
}
}
}
#Override
public void onReceive(Context context, Intent intent)
{
onUpdate(context);
}
public static void onUpdate(Context context)
{
Log.d("LOG", "Current app updated");
}
}
Getting information from all the users I could solve my situation this way.
All of them were right, with little points to notice:
In manifest:
<receiver android:name=".MyEventReceiver" >
<intent-filter android:priority="1000" >
<!--other actions I need-->
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package"/>
</intent-filter>
</receiver>
And code:
public class MyEventReceiver extends BroadcastReceiver
{
#Override public void onReceive(Context context, Intent intent)
{
if(Intent.ACTION_PACKAGE_REPLACED.equals(intent.getAction()))
{ if(intent.getData().getSchemeSpecificPart().equals(context.getPackageName()))
{ //Restart services.
}
}
}
}
In my Android release (2.3 Gingerbread) I was not able to use MY_PACKAGE_REPLACED but we solved using PACKAGE_REPLACED (will advise of any app been replaced) but asking if it is ours with:
if(intent.getData().getSchemeSpecificPart().equals(context.getPackageName()))
{
}
Thanks to all
I just want to add my own two pence worth here, because I had problems getting this to work and debugging it .. I am using a Lollipop device:
This is the code I used:
<receiver android:name=".services.OnUpdateReceiver">
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
To debug it, you need to make sure you have installed the update on your phone, just via Eclipse debug is fine, and opened the app at least one time, then you can simply edit your debug configuration:
eclipse > run > debug configurations > launch action (do nothing) > then F11 like normal
I confirmed it worked by writing a small file to the SD card
Simple Manifest working in all version :
<receiver
android:name=".Receiver"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
Are you trying it on a API>=12 device/emulator? This broadcast will not be sent on prior cases as it is API 12. If you need your app to receive this for Pre-ICS and the old honey comb devices,
try:
if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
if (!intent.getData().getSchemeSpecificPart()
.equals(context.getPackageName())) {
return;
}
}
Actually, your app will start twice when you install your apk.
Once for each receiver that you have set.
When you are Listening to android.intent.action.PACKAGE_REPLACED then you need to check for the package-name in the intent.getData()
Note that intent.getData() will be Null when it is from
android.intent.action.MY_PACKAGE_REPLACED
I think using one of them is enough.
I ran into this problem when I was building and installing my app from Android Studio.
When doing normal build & run (without debugging), I fixed the problem by turning off Instant Run.
When doing a build & run with debugging, I couldn't find a way to fix the problem.
You need to add data scheme to the intent filter like below
<receiver android:name=".MyEventReceiver" >
<intent-filter android:priority="1000" >
<action android:name="android.intent.action.ACTION_MY_PACKAGE_REPLACED" />
<data android:scheme="package"/>
</intent-filter>
</receiver>
I have made a simple widget with stretchable layout, and want to allow users to put widgets with different sizes using the same layout and the same widget provider class.
I have found, that I need to create 4 widget_provider_x_y.xml files (x and y are values 2,2 2,3 3,2 3,3) that is very similar except width and height.
Also I have found, that I need to create multiple copies of WidgetProvider classes that is exactly the same (except names). If I try to use single WidgetProvider class - I will see only one widget in widgets list.
I have a question:
- why do I need to copy java class? It is the same, because layout is the same, and I do not want to have 4 more files in projects that I need to update simultaneously.
Here is a part of my androidManifest.xml:
<receiver android:name=".SimpleNoteWidgetProvider_3_2" android:label="#string/app_widget_3_2">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="ACTION_WIDGET_UPDATE_FROM_ACTIVITY"/>
<action android:name="ACTION_WIDGET_UPDATE_FROM_WIDGET"/>
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="#xml/widget_provider_3_2"/>
</receiver>
<receiver android:name=".SimpleNoteWidgetProvider_3_3" android:label="#string/app_widget_3_3">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="ACTION_WIDGET_UPDATE_FROM_ACTIVITY"/>
<action android:name="ACTION_WIDGET_UPDATE_FROM_WIDGET"/>
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="#xml/widget_provider_3_3"/>
</receiver>
You don't have to copy your WidgetProvider class n times to get n widget sizes. Just inherit from the main WidgetProvider class like this:
public class SimpleNoteWidgetProvider_3_2 extends SimpleNoteWidgetProvider {
}
and
public class SimpleNoteWidgetProvider_3_3 extends SimpleNoteWidgetProvider {
}