In creating a watch face for Android Wear, I would like to have a simple configuration (a toggle switch?) that set which mode the user wanted the watch face to looks like (for example, white or black watch face).
I would prefer the toggle switch to be on the watch itself, prefer not to communicate with the phone for such simple action, and hopefully to avoid all the GoogleApiClient communications between watch and phones. Is there a way to do this easily, similar to doing Settings or SharedPreferences on Android?
I tried using a Broadcast receiver. I can get the changes in Broadcast receiver, but how do I get the CanvasWatchFaceService.Engine to update?
Yes, that's possible.
You have to follow this documentation.
First Create an Activity displaying the settings you want the user to change.
Then in your Manifest file, add this meta data to your Watchface service:
<meta-data
android:name=
"com.google.android.wearable.watchface.wearableConfigurationAction"
android:value=
"com.example.android.wearable.watchface.CONFIG_DIGITAL" />
And this IntentFilter to your Activity:
<intent-filter>
<action android:name=
"com.example.android.wearable.watchface.CONFIG_DIGITAL" />
<category android:name=
"com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
Of course, you will have to replace "com.example.android" by your package name.
Then a small setting icon will appear below your watchface preview in the Watchface selector screen.
Do not forget to synchronize the setting between your Activity and Watchface in order to make it appear instantly (with a BroadcastReceiver for example)
I addressed this with a LocalBroadcastManager that registers 3 intents
Get Initial Data, sent from Config Activity, expected by Watch Service
Initial Data, sent by Watch Service in response to the message above
Data Changed, sent by Config Activity when user makes selections.
Everything is wrapped in a single class which exposes two interfaces for interactions (one for Watch Service, the other for Config Activity. Probably not the easiest solution but the best I could come up with after 3 days of digging :(
For the record, here is the class sharing 2 variables (bezelMode and time24).
You will need to instantiate it from your watch service (implementing WatchConfig.Service) and you configuration activity (implementing WatchConfig.Editor)
Communication is based on LocalBroadcastManager
public class WatchConfig {
// private static final String TAG = "Config";
// Used when data has changed
public static final String CONFIG_DATA_CHANGED = "/config/changed";
// Used to provide initial data
public static final String CONFIG_INITIAL_DATA = "/config/inital-data";
// Used to query initial data
public static final String CONFIG_INITIAL_QUERY = "/config/initial-query";
private int m_BezelMode;
private boolean m_Time24;
private LocalBroadcastManager localBroadcastManager;
BroadcastReceiver broadcastReceiverDataChanged;
BroadcastReceiver broadcastReceiverInitialDataRequest;
BroadcastReceiver broadcastReceiverInitialData;
private Service service;
private Editor editor;
WatchConfig(Context context, Service service) {
initialize( context, service, null);
}
WatchConfig(Context context, Editor editor) {
initialize( context, null, editor);
}
void initialize( Context context, Service service, Editor editor) {
this.localBroadcastManager = LocalBroadcastManager.getInstance( context);
this.service = service;
this.editor = editor;
}
interface Service {
void onConfigDataUpdated(boolean time24, int bezelMode);
void onConfigInitialRequest();
}
interface Editor {
void onConfigInitialize(boolean time24, int bezelMode);
}
/**
* Registers all proper receivers
*/
public void connect() {
if( this.service != null) {
IntentFilter intentFilterDataChanged = new IntentFilter(CONFIG_DATA_CHANGED);
this.broadcastReceiverDataChanged = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Log.d(TAG,"Data Changed Notification");
service.onConfigDataUpdated(intent.getBooleanExtra("time24", true), intent.getIntExtra("bezel", 24));
}
};
this.localBroadcastManager.registerReceiver(broadcastReceiverDataChanged, intentFilterDataChanged);
IntentFilter intentFilterInitialDataRequesy = new IntentFilter(CONFIG_INITIAL_QUERY);
this.broadcastReceiverInitialDataRequest = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Log.d(TAG,"Initial Query Notification");
service.onConfigInitialRequest();
}
};
this.localBroadcastManager.registerReceiver(broadcastReceiverInitialDataRequest, intentFilterInitialDataRequesy);
} else {
this.broadcastReceiverDataChanged = null;
this.broadcastReceiverInitialDataRequest = null;
}
if( this.editor != null) {
IntentFilter intentFilterInitalData = new IntentFilter(CONFIG_INITIAL_DATA);
this.broadcastReceiverInitialData = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Log.d(TAG,"Initial Data notification");
editor.onConfigInitialize(intent.getBooleanExtra("time24", true), intent.getIntExtra("bezel", 24));
}
};
this.localBroadcastManager.registerReceiver(broadcastReceiverInitialData, intentFilterInitalData);
// Editors need intial data
Intent intentInitialDataRequest = new Intent( CONFIG_INITIAL_QUERY);
this.localBroadcastManager.sendBroadcast( intentInitialDataRequest);
} else {
this.broadcastReceiverInitialData = null;
}
}
public void disconnect() {
if( this.broadcastReceiverDataChanged != null) {
this.localBroadcastManager.unregisterReceiver(this.broadcastReceiverDataChanged);
}
if( this.broadcastReceiverInitialDataRequest != null) {
this.localBroadcastManager.unregisterReceiver(this.broadcastReceiverInitialDataRequest);
}
if( this.broadcastReceiverInitialData != null) {
this.localBroadcastManager.unregisterReceiver(this.broadcastReceiverInitialData);
}
}
/**
* Used to publish changes in configuration
*/
protected void publishInitialData(boolean time24, int bezel) {
this.m_Time24 = time24;
this.m_BezelMode = bezel;
Intent intent = new Intent( CONFIG_INITIAL_DATA);
intent.putExtra("time24", this.m_Time24);
intent.putExtra("bezel", this.m_BezelMode);
this.localBroadcastManager.sendBroadcast(intent);
}
/**
* Used to publish changes in configuration
*/
protected void publishUpdate() {
Intent intent = new Intent( CONFIG_DATA_CHANGED);
intent.putExtra("time24", this.m_Time24);
intent.putExtra("bezel", this.m_BezelMode);
this.localBroadcastManager.sendBroadcast(intent);
}
public void setTime24(boolean time24) {
this.m_Time24 = time24;
}
public void setBezelMode(int bezelMode) {
this.m_BezelMode = bezelMode;
}
}
Related
I created a Broadcast Receiver (BR) in a service that will react to incoming SMS with specific number and body. I only need it to receive for a few seconds/minutes after user action, that's why I didn't registered it in manifest or activity (user may close it). BR has two parts, automatic (which works fine) and manual which should launch MainActivity and start a Dialog. I know that Dialog can't be started from BR and thats why I created a Listener, but my problem is that it is always null after service starts. It has value in onCreate of my MainActivity, but when service starts it changes to null, and I understand why (serivce re-initalize the Listener listener). I even tryed to put initialised listener value to SharedPrefs and restore it after, but when I try to store it with json it only stores null again. So how do I make my listener != null??? These are the relevant parts of my code:
MainActivity
onCreate {
SMSService smsReceiver = new SMSService();
smsReceiver.setListener(new SMSService.Listener() { //here listener from service is != null
#Override
public void onTextReceived(String s) {
dialogS(s); // totaly different dialog
}
});
...
mDialogBuilder = new AlertDialog.Builder(this);
...
.setPositiveButton(new OnClick...
Intent servisIntent = new Intent(MainActivity.this, SMSService.class);
startService(servisIntent);
...
}
SMSService
private Listener listener; // and here it get null which is the problem
public int onStartCommand(Intent intent, int flags, int startId) {
...
SMSReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, MainActivity.class);
context.startActivity(i);
if (listener != null) {
listener.onTextReceived("4333");
}
}
void setListener(Listener listener) {
this.listener = listener; }
interface Listener {
void onTextReceived(String text);
}
Btw I also tried to put smsReceiver.setListener block of code in my Dialog .setPossitive onClickListener after calling startService hoping it would initiate after service but nothing
Installing a listener mechanism with setter method in service is bad practice. You can use ResultReceiver to receive callback results from service. It is Parcelable, so it can be passed in an intent before service started
I have a simple application where I am trying to learn how to:
1) Call a web service
2) Parse the Data
3) Save it to SQLiteDatabase
4) Set-up a ContentObserver/Broadcaster to detect changes in an Activity and change the UI
So far, I have done everything but having trouble with step 4.
Code:
LaunchActivity which shows the layout containing the UI which I eventually want to change launches the Service class:
Intent intent = new Intent(this, DataService.class);
intent.putExtra("restMethodType", 0);
startService(intent);
The DataService class then runs the desired AsyncTask Rest Method:
public class DataService extends IntentService {
public DataService() {
super("DataService");
}
public DataService(String name) {
super(name);
}
#Override
protected void onHandleIntent(Intent intent) {
RestMethodType restMethodType = RestMethodType.values()[intent.getIntExtra("restMethodType", 0)];
switch (restMethodType) {
case UPCOMING_MATCH:
UpcomingMatchRestMethod restMethod = new UpcomingMatchRestMethod(getApplicationContext(), intent);
restMethod.execute();
break;
}
}
}
Within the onPostExecute method of the AsyncTask, I save the retrieved data in to the database and then broadcast the event:
long newRowId = database.insert(MatchContract.MatchEntry.TABLE_NAME, MatchContract.MatchEntry.COLUMN_NAME_LOCATION, values);
if(newRowId != -1){
mIntent.putExtra("matchId", newRowId);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(mIntent);
}
Back in the LaunchActivity, I set-up a receiver object:
DataReceiver mDataReceiver;
#Override
protected void onResume() {
super.onResume();
IntentFilter intentFilter = new IntentFilter("What Goes In Here?");
registerReceiver(mDataReceiver, intentFilter);
}
This is what the receiver looks like:
private class DataReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Do Stuff like update the UI
// Make sure to wrap any View objects in runOnUIThread
}
}
So what goes in the IntentFilter? And what else am I missing? I followed the Google Docs but getting a little confused.
Thank you in advance.
I can launch my app by:
Tapping on its icon in launcher
Registering "visible" intent-filter (I mean - user clicks for example "Send with.." then chooses my app)
Entering numeral code in dialer and "call" - "invisible" intent, user cannot choose app, he just enters code
Are there any other ways to launch my app? (I'm mostly interested in something else like "invisible" intent from paragraph 3).
Assume that we have clean device only with default system apps (most popular of Google apps are also counted as default) and my app
Ways for usual users are preferred, but more difficult approaches will be also useful
Variants, which can be used on one device (no other devices needed to approach) are preferred, but "more-than-one-device variants" will also be useful.
You can also run your app from Web browser :
<intent-filter>
<data android:scheme="my.special.scheme" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
You can launch your app on NFC transaction :
Into mainfest <uses-feature android:name="android.hardware.nfc" />
Read more about this here : LINK
You can also register a receiver and launch app when you receive sms with secret code in it :
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
Object messages[] = (Object[]) bundle.get("pdus");
SmsMessage smsMessage[] = new SmsMessage[messages.length];
for (int n = 0; n < messages.length; n++) {
smsMessage[n] = SmsMessage.createFromPdu((byte[]) messages[n]);
}
String text = smsMessage[0].getMessageBody();
if(text = "yoursecretcode") {
//launch the app
abortBroadcast(); //if you want to hide this messeage
}
}
Required permission : <uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
You can also register a receiver and launch app when you receive call from selected phone number :
public class ServiceReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
MyPhoneStateListener phoneListener=new MyPhoneStateListener();
TelephonyManager telephony = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(phoneListener,PhoneStateListener.LISTEN_CALL_STATE);
}
}
public class MyPhoneStateListener extends PhoneStateListener {
public void onCallStateChanged(int state,String incomingNumber){
switch(state){
case TelephonyManager.CALL_STATE_RINGING:
String numer = TelephonyManager.EXTRA_INCOMING_NUMBER;
// launch your app if 'numer' is ...
break;
}
}
}
You need to this READ_PHONE_STATE permission
You can also use shell to do this (phone must be rooted):
For example :
Runtime.getRuntime().exec("su");
Runtime.getRuntime ().exec ("am start -n com.android.calculator2/.Calculator");
Colleague "Arpan" wrote :
Tilt Your Phone and Wave your Hand (Basically using a Proximity Sensor
to launch App's Intent)
I give you code sample :
public class SensorActivity extends Service implements SensorEventListener {
private SensorManager mSensorManager;
private Sensor mProximity;
#Override
public final void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mProximity = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
}
#Override
public final void onAccuracyChanged(Sensor sensor, int accuracy) {
// Do something here if sensor accuracy changes.
}
#Override
public final void onSensorChanged(SensorEvent event) {
float distance = event.values[0];
if(!ss()) // LAUNCH YOUR APP IF ISN't RUNNNING
}
#Override
protected void onResume() {
// Register a listener for the sensor.
super.onResume();
mSensorManager.registerListener(this, mProximity, SensorManager.SENSOR_DELAY_NORMAL);
}
#Override
protected void onPause() {
// Be sure to unregister the sensor when the activity pauses.
super.onPause();
mSensorManager.unregisterListener(this);
}
}
private boolean ss() {
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if ("com.packagename.something.ActivityName".equals(service.service.getClassName())) {
return true;
}
}
return false;
}
"Arpan" wrote also :
Plug any usb devices and put an intent filter in the manifest (If usb
host mode available)
public static boolean isConnected(Context context) {
Intent intent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
return plugged == BatteryManager.BATTERY_PLUGGED_AC || plugged == BatteryManager.BATTERY_PLUGGED_USB;
}
You can paste this to Timer
I have edited Arpan's post, i added link about Gesture Search in Android® .
You can launch application using widget (when user click this, app will launch),
I give you widget class code snipet, more you can find here :
package com.helloandroid.countdownexample;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
public class CountdownWidget extends AppWidgetProvider {
#Override
public void onDeleted(Context context, int[] appWidgetIds) {
//called when widgets are deleted
//see that you get an array of widgetIds which are deleted
//so handle the delete of multiple widgets in an iteration
super.onDeleted(context, appWidgetIds);
}
#Override
public void onDisabled(Context context) {
super.onDisabled(context);
//runs when all of the instances of the widget are deleted from
//the home screen
//here you can do some setup
}
#Override
public void onEnabled(Context context) {
super.onEnabled(context);
//runs when all of the first instance of the widget are placed
//on the home screen
}
#Override
public void onClick() {
//your code to launch application...
}
#Override
public void onReceive(Context context, Intent intent) {
//all the intents get handled by this method
//mainly used to handle self created intents, which are not
//handled by any other method
//the super call delegates the action to the other methods
//for example the APPWIDGET_UPDATE intent arrives here first
//and the super call executes the onUpdate in this case
//so it is even possible to handle the functionality of the
//other methods here
//or if you don't call super you can overwrite the standard
//flow of intent handling
super.onReceive(context, intent);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
//runs on APPWIDGET_UPDATE
//here is the widget content set, and updated
//it is called once when the widget created
//and periodically as set in the metadata xml
//the layout modifications can be done using the AppWidgetManager
//passed in the parameter, we will discuss it later
//the appWidgetIds contains the Ids of all the widget instances
//so here you want likely update all of them in an iteration
//we will use only the first creation run
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
}
check if Headphones are plugged in
Whenever Headphones are plugged in an intent (ACTION_HEADSET_PLUG) will be fired. Check for this via BroadcastReceiver and start Acitivity
IntentFilter f = new IntentFilter();
f.addAction(Intent.ACTION_HEADSET_PLUG);
registerReceiver(headsetPlugReceiver, f);
public BroadcastReceiver headsetPlugReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// start new Activity or do something else
}
};
And in Manifest:
<receiver android:name="activity.to.receive.headplug.event">
<intent-filter>
<action android:name="android.intent.action.HEADSET_PLUG" />
</intent-filter>
</receiver>
Tilt Your Phone and Wave your Hand (Basically using a Proximity Sensor to launch App's Intent)
Screen Tap And/Or Gesture to launch an intent (you can read about this HERE)
Plug any usb devices and put an intent filter in the manifest (If usb host mode available)
I'm using C2DM, my BroadcastReceivers propagate the C2DM events to a local service. the service complete the registration by sending the id to my webserver pus it's responsible for letting the device know about new messages, however if the application (one of the activities) is up we want to send an intent to that activity with the new data so it can be updated, if not than the NotificationManager is used to notify the user.
The issue is, how to know the activity is running ? the Application object is not an option since the Service is part of the application it's obviously going to be present. unregister in the onDesroy of each application is also not an option since it may occur in orientation change...
Any standard way to get it done ?
Solution 1:
You can use ActivityManager for Checking if Activity is Running or not:
public boolean isActivityRunning() {
ActivityManager activityManager = (ActivityManager)Monitor.this.getSystemService (Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> activitys = activityManager.getRunningTasks(Integer.MAX_VALUE);
isActivityFound = false;
for (int i = 0; i < activitys.size(); i++) {
if (activitys.get(i).topActivity.toString().equalsIgnoreCase("ComponentInfo{com.example.testapp/com.example.testapp.Your_Activity_Name}")) {
isActivityFound = true;
}
}
return isActivityFound;
}
need to add the permission to your manifest..
<uses-permission android:name="android.permission.GET_TASKS"/>
Solution 2:
Your can use an static variable in your activity for which you want to check it's running or not and store it some where for access from your service or broadcast receiver as:
static boolean CurrentlyRunning= false;
public void onStart() {
CurrentlyRunning= true; //Store status of Activity somewhere like in shared //preference
}
public void onStop() {
CurrentlyRunning= false;//Store status of Activity somewhere like in shared //preference
}
I hope this was helpful!
The next approach would work well if you want to handle incoming Google Cloud message (C2DM) by your activity (if any is running) or issue a notification if no activities are running.
Register one BroadcastReceiver in the manifest file. This receiver will handle C2D messages whenever application not running. Register another BroadcastReceiver programmatically in your activity. This receiver will handle C2D messages whenever activity is running.
AndoroidManifest.xml
<receiver
android:name=".StaticReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.mypackage" />
</intent-filter>
</receiver>
MyReceiver.java
public class StaticReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Trigger a Notification
}
}
MyActivity.java
public class MyActivity extends ActionBarActivity {
#Override
protected void onResume() {
super.onResume();
final IntentFilter filter = new
IntentFilter("com.google.android.c2dm.intent.RECEIVE");
filter.addCategory("com.mypackage");
filter.setPriority(1);
registerReceiver(dynamicReceiver, filter,
"com.google.android.c2dm.permission.SEND", null);
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(dynamicReceiver);
}
private final BroadcastReceiver dynamicReceiver
= new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent) {
// TODO Handle C2DM
// blocks passing broadcast to StaticReceiver instance
abortBroadcast();
}
};
}
Note! To catch broadcasts first, the priority of dynamicReceiver IntentFilter must be higher than priority of StaticReceiver instance IntentFilter (default priority is '0').
PS. It looks like broadcasts issued by Google Cloud Messaging Service are ordered broadcasts. Original idea author: CommonsWare
Copied from here.
you can use a static variable within the activity.
class MyActivity extends Activity {
static boolean active = false;
public void onStart() {
active = true;
}
public void onStop() {
active = false;
}
}
Easiest way to check that whether an Activity is running or not is:
Context context = MyActivity.this;
if (! ((Activity) context).isFinishing()) {
// Activity is running
} else {
// Activity has been finished
}
Note: If activity is not running you should not perform any UI related operation.
Can u help me find out why the registration of broadcast receiver returns null?
this is the code:
ScoIntent = new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
sReceiver = new ScoReceiver(context, Tmp);
if (context.registerReceiver(sReceiver, ScoIntent) == null) {
Log("FBR.GetBlueConnect:Error", "Can not find receiver ACTION_CONNECTION_STATE_CHANGED");
HFS.DisplayText("Can not connect to Bluetooth Headset, Please Exit", true);
}
and this is the reciver:
class ScoReceiver extends BroadcastReceiver {
public ScoReceiver(Context mcontext, Tools t){
bContext = mcontext;
tools = t;
}
#Override
public void onReceive(Context context, Intent arg1) {
tools.Log("ScoReceiver:onReceive", "In");
//arg1 = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
String action = arg1.getAction();
tools.Log("ScoReceiver:onReceive", ">>> Bluetooth SCO state changed !!! ");
if(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
int status = arg1.getIntExtra(BluetoothHeadset.EXTRA_STATE, AudioManager.SCO_AUDIO_STATE_ERROR );
}
The javadocs say,
Returns the first sticky intent found that matches filter, or null if
there are none.
Does this receiver have a sticky intent? Here's a post that talks about the difference between a stick and non-sticky intent,
what is the difference between sendStickyBroadcast and sendBroadcast in Android