mirror of
https://github.com/MatomoCamp/matomocamp-companion-android.git
synced 2024-09-19 16:13:46 +02:00
Added Android Beam support to allow sharing sessions using NFC, in all
activities showing sessions.
This commit is contained in:
parent
86e69f2856
commit
f78b9576fc
8 changed files with 232 additions and 13 deletions
|
@ -9,6 +9,7 @@
|
|||
android:targetSdkVersion="19" />
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
|
||||
<!-- Permissions required for alarms -->
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
@ -46,7 +47,15 @@
|
|||
android:value=".activities.MainActivity" />
|
||||
</activity>
|
||||
<activity android:name=".activities.TrackScheduleEventActivity" />
|
||||
<activity android:name=".activities.EventDetailsActivity" />
|
||||
<activity android:name=".activities.EventDetailsActivity" >
|
||||
<intent-filter>
|
||||
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:mimeType="application/be.digitalia.fosdem" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".activities.PersonInfoActivity" />
|
||||
<activity
|
||||
android:name=".activities.SearchResultActivity"
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
|
||||
<!-- Errors -->
|
||||
<string name="error_title">Error</string>
|
||||
<string name="event_not_found_error">Unable to load the session details.\nMake sure the database has been updated to the latest version.</string>
|
||||
<string name="schedule_loading_error">An error occurred while updating the schedule. Please try again later.</string>
|
||||
<string name="search_length_error">The minimum search text length is 3 chars.</string>
|
||||
|
||||
|
|
|
@ -5,17 +5,20 @@ import android.content.Intent;
|
|||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v7.app.ActionBarActivity;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
import be.digitalia.fosdem.R;
|
||||
import be.digitalia.fosdem.db.DatabaseManager;
|
||||
import be.digitalia.fosdem.fragments.EventDetailsFragment;
|
||||
import be.digitalia.fosdem.loaders.LocalCacheLoader;
|
||||
import be.digitalia.fosdem.model.Event;
|
||||
import be.digitalia.fosdem.utils.NfcUtils;
|
||||
import be.digitalia.fosdem.utils.NfcUtils.CreateNfcAppDataCallback;
|
||||
|
||||
/**
|
||||
* Displays a single event passed either as a complete Parcelable object in extras or as an id in data.
|
||||
|
@ -23,7 +26,7 @@ import be.digitalia.fosdem.model.Event;
|
|||
* @author Christophe Beyls
|
||||
*
|
||||
*/
|
||||
public class EventDetailsActivity extends ActionBarActivity implements LoaderCallbacks<Event> {
|
||||
public class EventDetailsActivity extends ActionBarActivity implements LoaderCallbacks<Event>, CreateNfcAppDataCallback {
|
||||
|
||||
public static final String EXTRA_EVENT = "event";
|
||||
|
||||
|
@ -38,11 +41,11 @@ public class EventDetailsActivity extends ActionBarActivity implements LoaderCal
|
|||
|
||||
getSupportActionBar().setTitle(R.string.event_details);
|
||||
|
||||
event = getIntent().getParcelableExtra(EXTRA_EVENT);
|
||||
Event event = getIntent().getParcelableExtra(EXTRA_EVENT);
|
||||
|
||||
if (event != null) {
|
||||
// The event has been passed as parameter, it can be displayed immediately
|
||||
initActionBar();
|
||||
initEvent(event);
|
||||
if (savedInstanceState == null) {
|
||||
Fragment f = EventDetailsFragment.newInstance(event);
|
||||
getSupportFragmentManager().beginTransaction().add(R.id.content, f).commit();
|
||||
|
@ -54,11 +57,14 @@ public class EventDetailsActivity extends ActionBarActivity implements LoaderCal
|
|||
}
|
||||
|
||||
/**
|
||||
* Initialize event-related ActionBar configuration after the event has been loaded.
|
||||
* Initialize event-related configuration after the event has been loaded.
|
||||
*/
|
||||
private void initActionBar() {
|
||||
private void initEvent(Event event) {
|
||||
this.event = event;
|
||||
// Enable up navigation only after getting the event details
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
// Enable Android Beam
|
||||
NfcUtils.setAppDataPushMessageCallbackIfAvailable(this, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -83,6 +89,11 @@ public class EventDetailsActivity extends ActionBarActivity implements LoaderCal
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] createNfcAppData() {
|
||||
return String.valueOf(event.getId()).getBytes();
|
||||
}
|
||||
|
||||
private static class EventLoader extends LocalCacheLoader<Event> {
|
||||
|
||||
private final long eventId;
|
||||
|
@ -100,23 +111,32 @@ public class EventDetailsActivity extends ActionBarActivity implements LoaderCal
|
|||
|
||||
@Override
|
||||
public Loader<Event> onCreateLoader(int id, Bundle args) {
|
||||
return new EventLoader(this, Long.parseLong(getIntent().getDataString()));
|
||||
Intent intent = getIntent();
|
||||
String eventIdString;
|
||||
if (NfcUtils.hasAppData(intent)) {
|
||||
// NFC intent
|
||||
eventIdString = new String(NfcUtils.extractAppData(intent));
|
||||
} else {
|
||||
// Normal in-app intent
|
||||
eventIdString = intent.getDataString();
|
||||
}
|
||||
return new EventLoader(this, Long.parseLong(eventIdString));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Event> loader, Event data) {
|
||||
if (data == null) {
|
||||
// Event not found, quit
|
||||
Toast.makeText(this, getString(R.string.event_not_found_error), Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
event = data;
|
||||
initActionBar();
|
||||
initEvent(data);
|
||||
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
if (fm.findFragmentById(R.id.content) == null) {
|
||||
Fragment f = EventDetailsFragment.newInstance(event);
|
||||
Fragment f = EventDetailsFragment.newInstance(data);
|
||||
fm.beginTransaction().add(R.id.content, f).commitAllowingStateLoss();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ import be.digitalia.fosdem.fragments.TrackScheduleListFragment;
|
|||
import be.digitalia.fosdem.model.Day;
|
||||
import be.digitalia.fosdem.model.Event;
|
||||
import be.digitalia.fosdem.model.Track;
|
||||
import be.digitalia.fosdem.utils.NfcUtils;
|
||||
import be.digitalia.fosdem.utils.NfcUtils.CreateNfcAppDataCallback;
|
||||
|
||||
/**
|
||||
* Track Schedule container, works in both single pane and dual pane modes.
|
||||
|
@ -23,7 +25,7 @@ import be.digitalia.fosdem.model.Track;
|
|||
* @author Christophe Beyls
|
||||
*
|
||||
*/
|
||||
public class TrackScheduleActivity extends ActionBarActivity implements TrackScheduleListFragment.Callbacks {
|
||||
public class TrackScheduleActivity extends ActionBarActivity implements TrackScheduleListFragment.Callbacks, CreateNfcAppDataCallback {
|
||||
|
||||
public static final String EXTRA_DAY = "day";
|
||||
public static final String EXTRA_TRACK = "track";
|
||||
|
@ -33,6 +35,7 @@ public class TrackScheduleActivity extends ActionBarActivity implements TrackSch
|
|||
private Day day;
|
||||
private Track track;
|
||||
private boolean isTabletLandscape;
|
||||
private Event lastSelectedEvent;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -72,6 +75,19 @@ public class TrackScheduleActivity extends ActionBarActivity implements TrackSch
|
|||
}
|
||||
}
|
||||
trackScheduleListFragment.setSelectionEnabled(isTabletLandscape);
|
||||
|
||||
if (isTabletLandscape) {
|
||||
// Enable Android Beam
|
||||
NfcUtils.setAppDataPushMessageCallbackIfAvailable(this, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] createNfcAppData() {
|
||||
if (lastSelectedEvent == null) {
|
||||
return null;
|
||||
}
|
||||
return String.valueOf(lastSelectedEvent.getId()).getBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -88,6 +104,8 @@ public class TrackScheduleActivity extends ActionBarActivity implements TrackSch
|
|||
public void onEventSelected(int position, Event event) {
|
||||
if (isTabletLandscape) {
|
||||
// Tablet mode: Show event details in the right pane fragment
|
||||
lastSelectedEvent = event;
|
||||
|
||||
FragmentManager fm = getSupportFragmentManager();
|
||||
EventDetailsFragment currentFragment = (EventDetailsFragment) fm.findFragmentById(R.id.event);
|
||||
if (event != null) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package be.digitalia.fosdem.activities;
|
|||
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.provider.BaseColumns;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
||||
|
@ -18,6 +19,8 @@ import be.digitalia.fosdem.fragments.EventDetailsFragment;
|
|||
import be.digitalia.fosdem.loaders.TrackScheduleLoader;
|
||||
import be.digitalia.fosdem.model.Day;
|
||||
import be.digitalia.fosdem.model.Track;
|
||||
import be.digitalia.fosdem.utils.NfcUtils;
|
||||
import be.digitalia.fosdem.utils.NfcUtils.CreateNfcAppDataCallback;
|
||||
|
||||
import com.viewpagerindicator.PageIndicator;
|
||||
|
||||
|
@ -27,7 +30,7 @@ import com.viewpagerindicator.PageIndicator;
|
|||
* @author Christophe Beyls
|
||||
*
|
||||
*/
|
||||
public class TrackScheduleEventActivity extends ActionBarActivity implements LoaderCallbacks<Cursor> {
|
||||
public class TrackScheduleEventActivity extends ActionBarActivity implements LoaderCallbacks<Cursor>, CreateNfcAppDataCallback {
|
||||
|
||||
public static final String EXTRA_DAY = "day";
|
||||
public static final String EXTRA_TRACK = "track";
|
||||
|
@ -69,6 +72,9 @@ public class TrackScheduleEventActivity extends ActionBarActivity implements Loa
|
|||
bar.setTitle(R.string.event_details);
|
||||
bar.setSubtitle(track.getName());
|
||||
|
||||
// Enable Android Beam
|
||||
NfcUtils.setAppDataPushMessageCallbackIfAvailable(this, this);
|
||||
|
||||
setCustomProgressVisibility(true);
|
||||
getSupportLoaderManager().initLoader(EVENTS_LOADER_ID, null, this);
|
||||
}
|
||||
|
@ -77,6 +83,18 @@ public class TrackScheduleEventActivity extends ActionBarActivity implements Loa
|
|||
progress.setVisibility(isVisible ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] createNfcAppData() {
|
||||
if (adapter.getCount() == 0) {
|
||||
return null;
|
||||
}
|
||||
long eventId = adapter.getItemId(pager.getCurrentItem());
|
||||
if (eventId == -1L) {
|
||||
return null;
|
||||
}
|
||||
return String.valueOf(eventId).getBytes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
|
@ -145,5 +163,12 @@ public class TrackScheduleEventActivity extends ActionBarActivity implements Loa
|
|||
cursor.moveToPosition(position);
|
||||
return EventDetailsFragment.newInstance(DatabaseManager.toEvent(cursor));
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
if (!cursor.moveToPosition(position)) {
|
||||
return -1L;
|
||||
}
|
||||
return cursor.getLong(cursor.getColumnIndexOrThrow(BaseColumns._ID));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
28
src/be/digitalia/fosdem/utils/NfcReceiverUtils.java
Normal file
28
src/be/digitalia/fosdem/utils/NfcReceiverUtils.java
Normal file
|
@ -0,0 +1,28 @@
|
|||
package be.digitalia.fosdem.utils;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Intent;
|
||||
import android.nfc.NdefMessage;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.os.Build;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* NFC helper methods for receiving data sent by NfcSenderUtils. This class wraps API 10+ code.
|
||||
*
|
||||
* @author Christophe Beyls
|
||||
*
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.GINGERBREAD_MR1)
|
||||
class NfcReceiverUtils {
|
||||
|
||||
public static boolean hasAppData(Intent intent) {
|
||||
return NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction());
|
||||
}
|
||||
|
||||
public static byte[] extractAppData(Intent intent) {
|
||||
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
|
||||
NdefMessage msg = (NdefMessage) rawMsgs[0];
|
||||
return msg.getRecords()[0].getPayload();
|
||||
}
|
||||
}
|
51
src/be/digitalia/fosdem/utils/NfcSenderUtils.java
Normal file
51
src/be/digitalia/fosdem/utils/NfcSenderUtils.java
Normal file
|
@ -0,0 +1,51 @@
|
|||
package be.digitalia.fosdem.utils;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import be.digitalia.fosdem.utils.NfcUtils.CreateNfcAppDataCallback;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.nfc.NdefMessage;
|
||||
import android.nfc.NdefRecord;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.nfc.NfcEvent;
|
||||
import android.nfc.NfcAdapter.CreateNdefMessageCallback;
|
||||
import android.os.Build;
|
||||
|
||||
/**
|
||||
* NFC helper methods for Android Beam foreground push. This class wraps API 14+ code.
|
||||
*
|
||||
* @author Christophe Beyls
|
||||
*
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||
class NfcSenderUtils {
|
||||
|
||||
public static boolean setAppDataPushMessageCallbackIfAvailable(Activity activity, final CreateNfcAppDataCallback callback) {
|
||||
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(activity);
|
||||
if (adapter == null) {
|
||||
return false;
|
||||
}
|
||||
final String packageName = activity.getPackageName();
|
||||
adapter.setNdefPushMessageCallback(new CreateNdefMessageCallback() {
|
||||
|
||||
@Override
|
||||
public NdefMessage createNdefMessage(NfcEvent event) {
|
||||
byte[] appData = callback.createNfcAppData();
|
||||
if (appData == null) {
|
||||
return null;
|
||||
}
|
||||
NdefRecord[] records = new NdefRecord[] { createMimeRecord("application/" + packageName, appData),
|
||||
NdefRecord.createApplicationRecord(packageName) };
|
||||
return new NdefMessage(records);
|
||||
}
|
||||
|
||||
}, activity);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static NdefRecord createMimeRecord(String mimeType, byte[] payload) {
|
||||
byte[] mimeBytes = mimeType.getBytes(Charset.forName("US-ASCII"));
|
||||
return new NdefRecord(NdefRecord.TNF_MIME_MEDIA, mimeBytes, new byte[0], payload);
|
||||
}
|
||||
}
|
67
src/be/digitalia/fosdem/utils/NfcUtils.java
Normal file
67
src/be/digitalia/fosdem/utils/NfcUtils.java
Normal file
|
@ -0,0 +1,67 @@
|
|||
package be.digitalia.fosdem.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
|
||||
/**
|
||||
* NFC helper methods compatible with all API levels.
|
||||
*
|
||||
* @author Christophe Beyls
|
||||
*
|
||||
*/
|
||||
public class NfcUtils {
|
||||
|
||||
/**
|
||||
* Implement this interface to create application-specific data to be shared through Android Beam.
|
||||
*/
|
||||
public interface CreateNfcAppDataCallback {
|
||||
/**
|
||||
*
|
||||
* @return The app data, or null if no data is currently available for sharing.
|
||||
*/
|
||||
public byte[] createNfcAppData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this method in an Activity, between onCreate() and onDestroy(), to make its content sharable using Android Beam if available. MIME type of the data
|
||||
* to share will be "application/" followed by the app's package name. Declare it in your Manifest's intent filters as the data type with an action of
|
||||
* android.nfc.action.NDEF_DISCOVERED to handle the NFC Intents on the receiver side.
|
||||
*
|
||||
* @param activity
|
||||
* @param callback
|
||||
* @return true if NFC is available and the content was made available, false if not.
|
||||
*/
|
||||
public static boolean setAppDataPushMessageCallbackIfAvailable(Activity activity, final CreateNfcAppDataCallback callback) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||
return NfcSenderUtils.setAppDataPushMessageCallbackIfAvailable(activity, callback);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the intent contains NFC NDEF application-specific data to be extracted.
|
||||
*
|
||||
* @param intent
|
||||
* @return
|
||||
*/
|
||||
public static boolean hasAppData(Intent intent) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
|
||||
return NfcReceiverUtils.hasAppData(intent);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts application-specific data sent through NFC from an intent. You must first ensure that the intent contains NFC data by calling hasAppData().
|
||||
*
|
||||
* @param intent
|
||||
* @return The extracted data
|
||||
*/
|
||||
public static byte[] extractAppData(Intent intent) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
|
||||
return NfcReceiverUtils.extractAppData(intent);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue