init commit
commit
a81902980b
@ -0,0 +1 @@
|
||||
/build
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,29 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /home/keith/.AndroidStudio1.4/sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
#-keep class org.codehaus.** {*;}
|
||||
#-dontwarn -dontwarn org.codehaus.**
|
||||
#-keep class com.ocpsoft.** {*;}
|
||||
#-dontwarn com.ocpsoft.**
|
||||
#-keep class java.nio.** {*;}
|
||||
#-dontwarn java.nio.**
|
||||
|
||||
#-dontwarn com.squareup.**
|
||||
#-dontwarn org.json.**
|
||||
|
||||
-dontwarn *
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
@ -0,0 +1,13 @@
|
||||
package us.keithirwin.tracman;
|
||||
|
||||
import android.app.Application;
|
||||
import android.test.ApplicationTestCase;
|
||||
|
||||
/**
|
||||
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
|
||||
*/
|
||||
public class ApplicationTest extends ApplicationTestCase<Application> {
|
||||
public ApplicationTest() {
|
||||
super(Application.class);
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="us.keithirwin.tracman"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:versionCode="@string/version_code"
|
||||
android:versionName="@string/version">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<uses-permission android:name="android.permission.BATTERY_STATS"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@drawable/logo_by"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".LoginActivity">
|
||||
<intent-filter android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:parentActivityName=".LoginActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="us.keithirwin.tracman.LoginActivity"/>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".SettingsActivity"
|
||||
android:label="@string/settings_name"
|
||||
android:parentActivityName=".MainActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".MainActivity"/>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".LocationService"
|
||||
android:exported="false">
|
||||
</service>
|
||||
|
||||
<receiver
|
||||
android:name=".BootReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON"/>
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -0,0 +1,109 @@
|
||||
package us.keithirwin.tracman;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatDelegate;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
|
||||
* to be used with AppCompat.
|
||||
*/
|
||||
public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
|
||||
|
||||
private AppCompatDelegate mDelegate;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
getDelegate().installViewFactory();
|
||||
getDelegate().onCreate(savedInstanceState);
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
getDelegate().onPostCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
public ActionBar getSupportActionBar() {
|
||||
return getDelegate().getSupportActionBar();
|
||||
}
|
||||
|
||||
public void setSupportActionBar(@Nullable Toolbar toolbar) {
|
||||
getDelegate().setSupportActionBar(toolbar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuInflater getMenuInflater() {
|
||||
return getDelegate().getMenuInflater();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(@LayoutRes int layoutResID) {
|
||||
getDelegate().setContentView(layoutResID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view) {
|
||||
getDelegate().setContentView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view, ViewGroup.LayoutParams params) {
|
||||
getDelegate().setContentView(view, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addContentView(View view, ViewGroup.LayoutParams params) {
|
||||
getDelegate().addContentView(view, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostResume() {
|
||||
super.onPostResume();
|
||||
getDelegate().onPostResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTitleChanged(CharSequence title, int color) {
|
||||
super.onTitleChanged(title, color);
|
||||
getDelegate().setTitle(title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
getDelegate().onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
getDelegate().onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
getDelegate().onDestroy();
|
||||
}
|
||||
|
||||
public void invalidateOptionsMenu() {
|
||||
getDelegate().invalidateOptionsMenu();
|
||||
}
|
||||
|
||||
private AppCompatDelegate getDelegate() {
|
||||
if (mDelegate == null) {
|
||||
mDelegate = AppCompatDelegate.create(this, null);
|
||||
}
|
||||
return mDelegate;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package us.keithirwin.tracman;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
public class BootReceiver extends BroadcastReceiver {
|
||||
public BootReceiver() {}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
// Starts location service on boot
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (prefs.getBoolean("pref_start_boot", true)) {
|
||||
context.startService(new Intent(context, LocationService.class));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,247 @@
|
||||
package us.keithirwin.tracman;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.location.Location;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.location.LocationListener;
|
||||
import com.google.android.gms.location.LocationRequest;
|
||||
import com.google.android.gms.location.LocationServices;
|
||||
|
||||
import com.github.nkzawa.emitter.Emitter;
|
||||
import com.github.nkzawa.socketio.client.IO;
|
||||
import com.github.nkzawa.socketio.client.Socket;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
public class LocationService extends Service implements GoogleApiClient.ConnectionCallbacks,
|
||||
GoogleApiClient.OnConnectionFailedListener, LocationListener {
|
||||
public LocationService() {}
|
||||
private String TAG = "LocationService";
|
||||
|
||||
private Socket mSocket;
|
||||
private String mUserID;
|
||||
private SharedPreferences sharedPref;
|
||||
Location mLastLocation;
|
||||
private GoogleApiClient mGoogleApiClient;
|
||||
private LocationRequest mLocationRequest;
|
||||
synchronized void buildGoogleApiClient() {
|
||||
mGoogleApiClient = new GoogleApiClient.Builder(this)
|
||||
.addConnectionCallbacks(this)
|
||||
.addOnConnectionFailedListener(this)
|
||||
.addApi(LocationServices.API)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private NotificationManager mNotificationManager;
|
||||
private final NotificationCompat.Builder mNotificationBuilder = new NotificationCompat.Builder(this);
|
||||
private void setupNotifications(Boolean persist) {
|
||||
if (mNotificationManager == null) {
|
||||
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
}
|
||||
PendingIntent notificationIntent = PendingIntent.getActivity(this, 0,
|
||||
new Intent(this, SettingsActivity.class)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP),
|
||||
0);
|
||||
mNotificationBuilder
|
||||
.setPriority(-1)
|
||||
.setSmallIcon(R.drawable.logo_white)
|
||||
// .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.logo_by))
|
||||
.setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setContentTitle(getText(R.string.app_name))
|
||||
// .setWhen(System.currentTimeMillis())
|
||||
.setContentIntent(notificationIntent)
|
||||
.setOngoing(persist);
|
||||
}
|
||||
private void showNotification(CharSequence text, Boolean active) {
|
||||
mNotificationBuilder
|
||||
.setTicker(text)
|
||||
.setContentText(text);
|
||||
if (active) {
|
||||
mNotificationBuilder.setSmallIcon(R.drawable.logo_white);
|
||||
} else {
|
||||
mNotificationBuilder.setSmallIcon(R.drawable.logo_trans);
|
||||
}
|
||||
if (mNotificationManager != null) {
|
||||
mNotificationManager.notify(1, mNotificationBuilder.build());
|
||||
}
|
||||
}
|
||||
|
||||
private final BroadcastReceiver LowPowerReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
connectLocationUpdates(300, LocationRequest.PRIORITY_NO_POWER);
|
||||
Log.d(TAG, "Priority and interval lowered due to low power");
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
Log.d(TAG, "onCreate called");
|
||||
|
||||
// Get preferences
|
||||
sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
setupNotifications(true);
|
||||
showNotification(getText(R.string.connecting), false);
|
||||
Log.d(TAG, "Notification set up");
|
||||
|
||||
buildGoogleApiClient();
|
||||
Log.d(TAG, "Google API Client built");
|
||||
mGoogleApiClient.connect();
|
||||
Log.d(TAG, "Connected to Google API Client");
|
||||
|
||||
IntentFilter lowPowerFilter = new IntentFilter();
|
||||
lowPowerFilter.addAction("android.intent.action.BATTERY_LOW");
|
||||
registerReceiver(LowPowerReceiver, lowPowerFilter);
|
||||
Log.d(TAG, "LowPowerReceiver activated");
|
||||
|
||||
mUserID = sharedPref.getString("loggedInUserId", null);
|
||||
final String SERVER_ADDRESS = "https://tracman.org/";
|
||||
|
||||
// Connect to socket
|
||||
try {
|
||||
mSocket = IO.socket(SERVER_ADDRESS);
|
||||
mSocket.on("activate", onActivate);
|
||||
mSocket.connect();
|
||||
mSocket.emit("room", "app-"+mUserID);
|
||||
Log.d(TAG, "Connected to socket.io server "+SERVER_ADDRESS);
|
||||
} catch (URISyntaxException e) {
|
||||
showNotification(getText(R.string.server_connection_error), false);
|
||||
Log.e(TAG, "Failed to connect to sockets server " + SERVER_ADDRESS, e);
|
||||
}
|
||||
showNotification(getText(R.string.connected), false);
|
||||
}
|
||||
|
||||
private int getPrioritySetting() {
|
||||
return Integer.parseInt(sharedPref.getString("broadcast_priority", "100"));
|
||||
}
|
||||
|
||||
private int getIntervalSetting() {
|
||||
return Integer.parseInt(
|
||||
sharedPref.getString("broadcast_frequency",
|
||||
getResources().getString(R.string.pref_default_broadcast_frequency)));
|
||||
}
|
||||
|
||||
void connectLocationUpdates(Integer interval, Integer priority) {
|
||||
if (mLocationRequest != null) {
|
||||
mLocationRequest.setPriority(priority);
|
||||
mLocationRequest.setInterval(interval * 1000); // 1000 = 1 second
|
||||
} else{
|
||||
mLocationRequest = LocationRequest.create();
|
||||
connectLocationUpdates(getIntervalSetting(), getPrioritySetting());
|
||||
}
|
||||
|
||||
if (mGoogleApiClient.isConnected()) {
|
||||
LocationServices.FusedLocationApi.requestLocationUpdates(
|
||||
mGoogleApiClient,
|
||||
mLocationRequest,
|
||||
this);
|
||||
} else {
|
||||
mGoogleApiClient.connect();
|
||||
}
|
||||
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
|
||||
|
||||
if (mLastLocation != null) {
|
||||
onLocationChanged(mLastLocation);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnected(Bundle bundle) {
|
||||
Log.d(TAG, "onConnected called");
|
||||
|
||||
mLocationRequest = LocationRequest.create();
|
||||
connectLocationUpdates(getIntervalSetting(), getPrioritySetting());
|
||||
showNotification(getString(R.string.realtime_updates), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionFailed(ConnectionResult connectionResult) {
|
||||
Log.e(TAG, "onConnectionFailed: " + connectionResult);
|
||||
showNotification(getText(R.string.google_connection_error), false);
|
||||
buildGoogleApiClient();
|
||||
}
|
||||
|
||||
private Emitter.Listener onActivate = new Emitter.Listener() {
|
||||
@Override
|
||||
public void call(final Object... args) {
|
||||
if (args[0].toString().equals("true")) {
|
||||
Log.d(TAG, "Activating realtime updates");
|
||||
connectLocationUpdates(getIntervalSetting(), getPrioritySetting());
|
||||
showNotification(getString(R.string.realtime_updates), true);
|
||||
} else {
|
||||
Log.d(TAG, "Deactivating realtime updates");
|
||||
connectLocationUpdates(300, LocationRequest.PRIORITY_NO_POWER);
|
||||
showNotification(getString(R.string.occasional_updates), false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
JSONObject mLocationView = new JSONObject();
|
||||
try {
|
||||
mLocationView.put("usr", mUserID);
|
||||
mLocationView.put("lat", String.valueOf(location.getLatitude()));
|
||||
mLocationView.put("lon", String.valueOf(location.getLongitude()));
|
||||
mLocationView.put("dir", String.valueOf(location.getBearing()));
|
||||
mLocationView.put("spd", String.valueOf(location.getSpeed()));
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Failed to put JSON data");
|
||||
}
|
||||
mSocket.emit("app", mLocationView);
|
||||
// Log.v(TAG, "Location updated: " + mLocationView.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionSuspended(int i) {
|
||||
Log.d(TAG, "onConnectionSuspended called");
|
||||
showNotification(getText(R.string.google_connection_error), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
Log.d(TAG, "onDestroy executed");
|
||||
|
||||
mSocket.disconnect();
|
||||
Log.d(TAG, "Disconnected from sockets");
|
||||
|
||||
mGoogleApiClient.disconnect();
|
||||
Log.d(TAG, "Google API disconnected");
|
||||
|
||||
unregisterReceiver(LowPowerReceiver);
|
||||
Log.d(TAG, "LowPowerReceiver deactivated");
|
||||
|
||||
setupNotifications(false);
|
||||
showNotification(getText(R.string.disconnected), false);
|
||||
Log.d(TAG, "Notification changed");
|
||||
}
|
||||
}
|
@ -0,0 +1,239 @@
|
||||
package us.keithirwin.tracman;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.android.gms.auth.api.Auth;
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInResult;
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.SignInButton;
|
||||
import com.google.android.gms.common.api.GoogleApiClient;
|
||||
import com.google.android.gms.common.api.OptionalPendingResult;
|
||||
import com.google.android.gms.common.api.ResultCallback;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class LoginActivity extends AppCompatActivity implements
|
||||
GoogleApiClient.OnConnectionFailedListener,
|
||||
View.OnClickListener {
|
||||
private static final String TAG = "SignInActivity";
|
||||
private static final int RC_SIGN_IN = 9001;
|
||||
private final String SERVER_ADDRESS = "https://tracman.org/";
|
||||
private static final String GOOGLE_WEB_CLIENT_ID = "483494341936-hrn0ms1tebgdtfs5f4i6ebmkt3qmo16o.apps.googleusercontent.com";
|
||||
|
||||
private GoogleApiClient mGoogleApiClient;
|
||||
private ProgressDialog mProgressDialog;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// Set up layout
|
||||
setContentView(R.layout.activity_login);
|
||||
setTitle(R.string.login_name);
|
||||
TextView tv = (TextView) findViewById(R.id.login_description);
|
||||
tv.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
|
||||
// Configure sign-in to request the user's ID and basic profile, included in DEFAULT_SIGN_IN.
|
||||
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
|
||||
.requestIdToken(GOOGLE_WEB_CLIENT_ID)
|
||||
.build();
|
||||
|
||||
// Set up buttons
|
||||
SignInButton signInButton = (SignInButton) findViewById(R.id.google_sign_in_button);
|
||||
signInButton.setStyle(SignInButton.SIZE_WIDE, SignInButton.COLOR_DARK, gso.getScopeArray());
|
||||
|
||||
// Build a GoogleApiClient with access to the Google Sign-In API and the
|
||||
// options specified by gso.
|
||||
mGoogleApiClient = new GoogleApiClient.Builder(this)
|
||||
.enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */)
|
||||
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
|
||||
.build();
|
||||
|
||||
// Button listeners
|
||||
findViewById(R.id.google_sign_in_button).setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
if (getIntent().hasExtra("method")) {
|
||||
if (getIntent().getStringExtra("method").equals("signOut")) {
|
||||
Log.d(TAG, "Got intent to sign out");
|
||||
}
|
||||
|
||||
} else { // Try to sign in
|
||||
OptionalPendingResult<GoogleSignInResult> opr = Auth.GoogleSignInApi.silentSignIn(mGoogleApiClient);
|
||||
if (opr.isDone()) {
|
||||
// If the user's cached credentials are valid, the OptionalPendingResult will be "done"
|
||||
// and the GoogleSignInResult will be available instantly.
|
||||
Log.d(TAG, "Got cached sign-in");
|
||||
GoogleSignInResult result = opr.get();
|
||||
handleSignInResult(result);
|
||||
} else {
|
||||
// If the user has not previously signed in on this device or the sign-in has expired,
|
||||
// this asynchronous branch will attempt to sign in the user silently. Cross-device
|
||||
// single sign-on will occur in this branch.
|
||||
showProgressDialog();
|
||||
opr.setResultCallback(new ResultCallback<GoogleSignInResult>() {
|
||||
@Override
|
||||
public void onResult(GoogleSignInResult googleSignInResult) {
|
||||
hideProgressDialog();
|
||||
handleSignInResult(googleSignInResult);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
// Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...);
|
||||
if (requestCode == RC_SIGN_IN) {
|
||||
GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
|
||||
handleSignInResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
private void AuthenticateGoogle(final String token) throws Exception {
|
||||
final OkHttpClient client = new OkHttpClient();
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(SERVER_ADDRESS+"auth/google/idtoken?id_token="+token)
|
||||
.build();
|
||||
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(Request request, IOException throwable) {
|
||||
Log.e(TAG, "Failed to connect to server: " + SERVER_ADDRESS + "auth/google/idtoken?id_token=" + token);
|
||||
showError(R.string.server_connection_error);
|
||||
throwable.printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Response res) throws IOException {
|
||||
if (!res.isSuccessful()) {
|
||||
showError(R.string.login_no_user_error);
|
||||
res.body().close();
|
||||
throw new IOException("Unexpected code " + res);
|
||||
} else {
|
||||
Log.d(TAG, "Response code: " + res.code());
|
||||
String userString = res.body().string();
|
||||
System.out.println("Full response: " + userString);
|
||||
|
||||
String userID, userName;
|
||||
try {
|
||||
JSONObject user = new JSONObject(userString);
|
||||
userID = user.getString("_id");
|
||||
userName = user.getString("name");
|
||||
Log.v(TAG, "User retrieved with ID: " + userID);
|
||||
|
||||
} catch (JSONException e) {
|
||||
Log.e(TAG, "Unable to parse user JSON: ", e);
|
||||
Log.e(TAG, "JSON String used: " + userString);
|
||||
userID = null;
|
||||
userName = null;
|
||||
}
|
||||
Log.v(TAG, "UserID: " + userID);
|
||||
|
||||
// Save user as loggedInUser
|
||||
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
SharedPreferences.Editor editor = sharedPref.edit();
|
||||
editor.putString("loggedInUser", userString);
|
||||
editor.putString("loggedInUserId", userID);
|
||||
editor.putString("loggedInUserName", userName);
|
||||
editor.commit();
|
||||
|
||||
startActivity(new Intent(getBaseContext(), MainActivity.class));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleSignInResult(GoogleSignInResult result) {
|
||||
Log.d(TAG, "handleSignInResult:" + result.isSuccess());
|
||||
if (result.isSuccess()) { // Signed in successfully
|
||||
GoogleSignInAccount acct = result.getSignInAccount();
|
||||
String googleToken = acct.getIdToken();
|
||||
Log.v(TAG, "Google token: " + googleToken);
|
||||
try {
|
||||
AuthenticateGoogle(acct.getIdToken());
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error sending ID token to backend.", e);
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "Failed to log in: "+result.getStatus().getStatusCode());
|
||||
if (result.getStatus().getStatusCode()!=4) {
|
||||
showError(R.string.google_connection_error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void googleSignIn() {
|
||||
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
|
||||
startActivityForResult(signInIntent, RC_SIGN_IN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionFailed(ConnectionResult connectionResult) {
|
||||
showError(R.string.disconnected);
|
||||
Log.d(TAG, "onConnectionFailed:" + connectionResult);
|
||||
}
|
||||
|
||||
private void showProgressDialog() {
|
||||
if (mProgressDialog == null) {
|
||||
mProgressDialog = new ProgressDialog(this);
|
||||
mProgressDialog.setMessage(getString(R.string.loading));
|
||||
mProgressDialog.setIndeterminate(true);
|
||||
}
|
||||
mProgressDialog.show();
|
||||
}
|
||||
|
||||
private void hideProgressDialog() {
|
||||
if (mProgressDialog != null && mProgressDialog.isShowing()) {
|
||||
mProgressDialog.hide();
|
||||
}
|
||||
}
|
||||
|
||||
private void showError(final int errorText) {
|
||||
final TextView errorTextView = (TextView)findViewById(R.id.login_error);
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
errorTextView.setText(getText(errorText).toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.google_sign_in_button:
|
||||
googleSignIn();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,182 @@
|
||||
package us.keithirwin.tracman;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
|
||||
import values.AboutFragment;
|
||||
import values.MainFragment;
|
||||
|
||||
public class MainActivity extends AppCompatActivity implements
|
||||
NavigationDrawerFragment.NavigationDrawerCallbacks,
|
||||
MainFragment.OnMapButtonPressedListener,
|
||||
AboutFragment.OnBackButtonPressedListener {
|
||||
|
||||
private static final String TAG = "MainActivity";
|
||||
private CharSequence mTitle;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
NavigationDrawerFragment mNavigationDrawerFragment = (NavigationDrawerFragment)
|
||||
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
|
||||
mTitle = getTitle();
|
||||
|
||||
// Set up the drawer.
|
||||
mNavigationDrawerFragment.setUp(
|
||||
R.id.navigation_drawer,
|
||||
(DrawerLayout) findViewById(R.id.drawer_layout));
|
||||
|
||||
// Check if gps enabled and start location service
|
||||
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
if (sharedPref.getBoolean("gps_switch", false)) {
|
||||
Log.d(TAG, "Starting LocationService");
|
||||
this.startService(new Intent(this, LocationService.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNavigationDrawerItemSelected(int position) {
|
||||
// update the main content by replacing fragments
|
||||
Log.v(TAG, "onNavigationDrawerItemSelected() called");
|
||||
|
||||
Fragment fragment;
|
||||
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
|
||||
switch(position) {
|
||||
default:
|
||||
case 0:
|
||||
Log.d(TAG, "Sending intent to go to main fragment");
|
||||
|
||||
// Get user ID and name
|
||||
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
final String mUserID = sharedPref.getString("loggedInUserId", null);
|
||||
final String mUserName = sharedPref.getString("loggedInUserName", null);
|
||||
if (mUserID == null) {
|
||||
startActivity(new Intent(this, LoginActivity.class)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP));
|
||||
}
|
||||
|
||||
fragment = MainFragment.newInstance(mUserName, mUserID);
|
||||
break;
|
||||
case 1:
|
||||
Log.v(TAG, "Sending intent to go to Settings Activity");
|
||||
fragment = null;
|
||||
startActivity(new Intent(this, SettingsActivity.class)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP));
|
||||
break;
|
||||
case 2:
|
||||
Log.v(TAG, "Sending intent to go to about fragment");
|
||||
fragment = AboutFragment.newInstance();
|
||||
break;
|
||||
case 3:
|
||||
Log.v(TAG, "Sending intent to go to logout fragment");
|
||||
fragment = null;
|
||||
Log.d(TAG, "Sending intent to log out");
|
||||
|
||||
// Stop LocationService
|
||||
stopService(new Intent(this, LocationService.class));
|
||||
|
||||
// Send back to login screen
|
||||
startActivity(new Intent(this, LoginActivity.class)
|
||||
.putExtra("method", "signOut")
|
||||
.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP));
|
||||
break;
|
||||
}
|
||||
|
||||
if (fragment!=null) {
|
||||
fragmentManager.beginTransaction()
|
||||
.replace(R.id.container, fragment)
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
|
||||
public void onSectionAttached(int number) {
|
||||
switch (number) {
|
||||
case 0:
|
||||
mTitle = getString(R.string.main_name);
|
||||
// Toast.makeText(this, "main", Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case 1:
|
||||
// mTitle = getString(R.string.settings_name);
|
||||
// Log.d(TAG, "Sending intent to go to settings");
|
||||
// startActivity(new Intent(this, SettingsActivity.class)
|
||||
// .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP));
|
||||
break;
|
||||
case 2:
|
||||
mTitle = getString(R.string.about_name);
|
||||
break;
|
||||
// case 3:
|
||||
// break;
|
||||
}
|
||||
}
|
||||
|
||||
public void goBack() {
|
||||
onNavigationDrawerItemSelected(0);
|
||||
}
|
||||
|
||||
public void showMap(String UserId) {
|
||||
String url = "https://tracman.org/trac/id/" + UserId;
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse(url));
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
// public void restoreActionBar() {
|
||||
// ActionBar actionBar = getActionBar();
|
||||
// actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
|
||||
// actionBar.setDisplayShowTitleEnabled(true);
|
||||
// actionBar.setTitle(mTitle);
|
||||
// }
|
||||
|
||||
/**
|
||||
* A placeholder fragment containing a simple view.
|
||||
*/
|
||||
public static class PlaceholderFragment extends Fragment {
|
||||
/**
|
||||
* The fragment argument representing the section number for this
|
||||
* fragment.
|
||||
*/
|
||||
private static final String ARG_SECTION_NUMBER = "section_number";
|
||||
|
||||
public PlaceholderFragment() {}
|
||||
|
||||
/**
|
||||
* Returns a new instance of this fragment for the given section
|
||||
* number.
|
||||
*/
|
||||
public static PlaceholderFragment newInstance(int sectionNumber) {
|
||||
PlaceholderFragment fragment = new PlaceholderFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(ARG_SECTION_NUMBER, sectionNumber);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_main, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
((MainActivity) context).onSectionAttached(getArguments().getInt(ARG_SECTION_NUMBER));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,265 @@
|
||||
package us.keithirwin.tracman;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.ActionBarDrawerToggle;
|
||||
import android.support.v4.view.GravityCompat;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
/**
|
||||
* Fragment used for managing interactions for and presentation of a navigation drawer.
|
||||
* See the <a href="https://developer.android.com/design/patterns/navigation-drawer.html#Interaction">
|
||||
* design guidelines</a> for a complete explanation of the behaviors implemented here.
|
||||
*/
|
||||
public class NavigationDrawerFragment extends Fragment {
|
||||
private static final String TAG = "NavDrawerFrag";
|
||||
|
||||
/**
|
||||
* Remember the position of the selected item.
|
||||
*/
|
||||
private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position";
|
||||
|
||||
/**
|
||||
* A pointer to the current callbacks instance (the Activity).
|
||||
*/
|
||||
private NavigationDrawerCallbacks mCallbacks;
|
||||
|
||||
/**
|
||||
* Helper component that ties the action bar to the navigation drawer.
|
||||
*/
|
||||
private ActionBarDrawerToggle mDrawerToggle;
|
||||
|
||||
private DrawerLayout mDrawerLayout;
|
||||
private ListView mDrawerListView;
|
||||
private View mFragmentContainerView;
|
||||
|
||||
private int mCurrentSelectedPosition = 0;
|
||||
|
||||
// public NavigationDrawerFragment() {}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);
|
||||
}
|
||||
|
||||
// Select either the default item (0) or the last selected item.
|
||||
selectItem(mCurrentSelectedPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
// Indicate that this fragment would like to influence the set of actions in the action bar.
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
mDrawerListView = (ListView) inflater.inflate(
|
||||
R.layout.drawer, container, false);
|
||||
mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
selectItem(position);
|
||||
}
|
||||
});
|
||||
mDrawerListView.setAdapter(new ArrayAdapter<>(
|
||||
getActionBar().getThemedContext(),
|
||||
android.R.layout.simple_list_item_activated_1,
|
||||
// android.R.id.text1,
|
||||
new String[]{
|
||||
getString(R.string.main_name),
|
||||
getString(R.string.settings_name),
|
||||
getString(R.string.about_name),
|
||||
getString(R.string.logout_name)
|
||||
}));
|
||||
mDrawerListView.setItemChecked(mCurrentSelectedPosition, true);
|
||||
return mDrawerListView;
|
||||
}
|
||||
|
||||
// public boolean isDrawerOpen() {
|
||||
// return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Users of this fragment must call this method to set up the navigation drawer interactions.
|
||||
*
|
||||
* @param fragmentId The android:id of this fragment in its activity's layout.
|
||||
* @param drawerLayout The DrawerLayout containing this fragment's UI.
|
||||
*/
|
||||
public void setUp(int fragmentId, DrawerLayout drawerLayout) {
|
||||
mFragmentContainerView = getActivity().findViewById(fragmentId);
|
||||
mDrawerLayout = drawerLayout;
|
||||
|
||||
// set a custom shadow that overlays the main content when the drawer opens
|
||||
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
|
||||
// set up the drawer's list view with items and click listener
|
||||
|
||||
ActionBar actionBar = getActionBar();
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setHomeButtonEnabled(true);
|
||||
|
||||
// ActionBarDrawerToggle ties together the the proper interactions
|
||||
// between the navigation drawer and the action bar app icon.
|
||||
mDrawerToggle = new ActionBarDrawerToggle(
|
||||
getActivity(), /* host Activity */
|
||||
mDrawerLayout, /* DrawerLayout object */
|
||||
R.string.navigation_drawer_open, /* "open drawer" description for accessibility */
|
||||
R.string.navigation_drawer_close /* "close drawer" description for accessibility */
|
||||
) {
|
||||
@Override
|
||||
public void onDrawerClosed(View drawerView) {
|
||||
super.onDrawerClosed(drawerView);
|
||||
if (!isAdded()) {
|
||||
return;
|
||||
}
|
||||
getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu()
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDrawerOpened(View drawerView) {
|
||||
super.onDrawerOpened(drawerView);
|
||||
if (!isAdded()) {
|
||||
return;
|
||||
}
|
||||
getActivity().invalidateOptionsMenu(); // calls onPrepareOptionsMenu()
|
||||
}
|
||||
};
|
||||
|
||||
// Defer code dependent on restoration of previous instance state.
|
||||
mDrawerLayout.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mDrawerToggle.syncState();
|
||||
}
|
||||
});
|
||||
|
||||
mDrawerLayout.addDrawerListener(mDrawerToggle);
|
||||
}
|
||||
|
||||
private void selectItem(int position) {
|
||||
Log.v(TAG, "selectItem() called");
|
||||
mCurrentSelectedPosition = position;
|
||||
if (mDrawerListView != null) {
|
||||
Log.v(TAG, "mDrawerListView != null"); //
|
||||
mDrawerListView.setItemChecked(position, true);
|
||||
}
|
||||
if (mDrawerLayout != null) {
|
||||
Log.v(TAG, "mDrawerLayout != null"); //
|
||||
mDrawerLayout.closeDrawer(mFragmentContainerView);
|
||||
}
|
||||
if (mCallbacks != null) {
|
||||
Log.v(TAG, "mCallbacks != null");
|
||||
mCallbacks.onNavigationDrawerItemSelected(position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
try {
|
||||
mCallbacks = (NavigationDrawerCallbacks) context;
|
||||
} catch (ClassCastException e) {
|
||||
throw new ClassCastException("Activity must implement NavigationDrawerCallbacks.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
mCallbacks = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
// Forward the new configuration the drawer toggle component.
|
||||
mDrawerToggle.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
// Show the global app actions in the action bar. See also
|
||||
// showGlobalContextActionBar, which controls the top-left area of the action bar.
|
||||
inflater.inflate(R.menu.global, menu);
|
||||
showGlobalContextActionBar();
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (mDrawerToggle.onOptionsItemSelected(item)) {
|
||||
return true;
|
||||
}
|
||||
if (item.getItemId() == R.id.help_action) {
|
||||
Toast.makeText(getActivity(), "Help is on the way! ", Toast.LENGTH_SHORT).show();
|
||||
// TODO: help activity
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.suggestions_action) {
|
||||
String url = "https://tracman.org/suggestion";
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse(url));
|
||||
startActivity(i);
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.bugs_action) {
|
||||
String url = "https://tracman.org/bug?source=android";
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse(url));
|
||||
startActivity(i);
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Per the navigation drawer design guidelines, updates the action bar to show the global app
|
||||
* 'context', rather than just what's in the current screen.
|
||||
*/
|
||||
private void showGlobalContextActionBar() {
|
||||
ActionBar actionBar = getActionBar();
|
||||
actionBar.setDisplayShowTitleEnabled(true);
|
||||
actionBar.setTitle(R.string.app_name);
|
||||
}
|
||||
|
||||
private ActionBar getActionBar() {
|
||||
return ((AppCompatActivity) getActivity()).getSupportActionBar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callbacks interface that all activities using this fragment must implement.
|
||||
*/
|
||||
public interface NavigationDrawerCallbacks {
|
||||
/**
|
||||
* Called when an item in the navigation drawer is selected.
|
||||
*/
|
||||
void onNavigationDrawerItemSelected(int position);
|
||||
}
|
||||
}
|
@ -0,0 +1,232 @@
|
||||
package us.keithirwin.tracman;
|
||||
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@link PreferenceActivity} that presents a set of application settings. On
|
||||
* handset devices, settings are presented as a single list. On tablets,
|
||||
* settings are split by category, with category headers shown to the left of
|
||||
* the list of settings.
|
||||
* <p/>
|
||||
* See <a href="http://developer.android.com/design/patterns/settings.html">
|
||||
* Android Design: Settings</a> for design guidelines and the <a
|
||||
* href="http://developer.android.com/guide/topics/ui/settings.html">Settings
|
||||
* API Guide</a> for more information on developing a Settings UI.
|
||||
*/
|
||||
public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
private static final String TAG = "SettingsActivity";
|
||||
|
||||
/**
|
||||
* A preference value change listener that updates the preference's summary
|
||||
* to reflect its new value.
|
||||
*/
|
||||
private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object value) {
|
||||
String stringValue = value.toString();
|
||||
|
||||
if (preference instanceof ListPreference) {
|
||||
// For list preferences, look up the correct display value in
|
||||
// the preference's 'entries' list.
|
||||
ListPreference listPreference = (ListPreference) preference;
|
||||
int index = listPreference.findIndexOfValue(stringValue);
|
||||
|
||||
// Set the summary to reflect the new value.
|
||||
preference.setSummary(
|
||||
index >= 0
|
||||
? listPreference.getEntries()[index]
|
||||
: null);
|
||||
} else {
|
||||
// For all other preferences, set the summary to the value's
|
||||
// simple string representation.
|
||||
preference.setSummary(stringValue);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper method to determine if the device has an extra-large screen. For
|
||||
* example, 10" tablets are extra-large.
|
||||
*/
|
||||
private static boolean isXLargeTablet(Context context) {
|
||||
return (context.getResources().getConfiguration().screenLayout
|
||||
& Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a preference's summary to its value. More specifically, when the
|
||||
* preference's value is changed, its summary (line of text below the
|
||||
* preference title) is updated to reflect the value. The summary is also
|
||||
* immediately updated upon calling this method. The exact display format is
|
||||
* dependent on the type of preference.
|
||||
*
|
||||
*/
|
||||
private static void bindPreferenceSummaryToValue(Preference preference) {
|
||||
// Set the listener to watch for value changes.
|
||||
preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
|
||||
|
||||
// Trigger the listener immediately with the preference's
|
||||
// current value.
|
||||
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
|
||||
PreferenceManager
|
||||
.getDefaultSharedPreferences(preference.getContext())
|
||||
.getString(preference.getKey(), ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setupActionBar();
|
||||
Log.d(TAG, "activity onCreate called");
|
||||
|
||||
// Get User ID
|
||||
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
String mUserID = sharedPref.getString("loggedInUserId", null);
|
||||
if (mUserID == null) {
|
||||
startActivity(new Intent(this, LoginActivity.class).setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
Log.d(TAG, "onStop called");
|
||||
super.onStop();
|
||||
// Restart service so settings can take effect
|
||||
stopService(new Intent(this, LocationService.class));
|
||||
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
if (sharedPref.getBoolean("gps_switch", false)) {
|
||||
Log.d(TAG, "Starting LocationService");
|
||||
startService(new Intent(this, LocationService.class));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the {@link android.app.ActionBar}, if the API is available.
|
||||
*/
|
||||
private void setupActionBar() {
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
// Show the Up button in the action bar.
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean onIsMultiPane() {
|
||||
return isXLargeTablet(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public void onBuildHeaders(List<Header> target) {
|
||||
loadHeadersFromResource(R.xml.pref_headers, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId()==android.R.id.home) {
|
||||
// Respond to the action bar's Up/Home button
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method stops fragment injection in malicious applications.
|
||||
* Make sure to deny any unknown fragments here.
|
||||
*/
|
||||
protected boolean isValidFragment(String fragmentName) {
|
||||
return PreferenceFragment.class.getName().equals(fragmentName)
|
||||
|| GeneralPreferenceFragment.class.getName().equals(fragmentName);
|
||||
// || MapPreferenceFragment.class.getName().equals(fragmentName)
|
||||
// || NotificationPreferenceFragment.class.getName().equals(fragmentName);
|
||||
}
|
||||
|
||||
/**
|
||||
* This fragment shows general preferences only. It is used when the
|
||||
* activity is showing a two-pane settings UI.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public static class GeneralPreferenceFragment extends PreferenceFragment {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.pref_general);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
// Bind the summary of server address preference to its value
|
||||
bindPreferenceSummaryToValue(findPreference("broadcast_frequency"));
|
||||
bindPreferenceSummaryToValue(findPreference("broadcast_priority"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This fragment shows notification preferences only. It is used when the
|
||||
* activity is showing a two-pane settings UI.
|
||||
*/
|
||||
// @TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
// public static class NotificationPreferenceFragment extends PreferenceFragment {
|
||||
// @Override
|
||||
// public void onCreate(Bundle savedInstanceState) {
|
||||
// super.onCreate(savedInstanceState);
|
||||
// addPreferencesFromResource(R.xml.pref_notification);
|
||||
// setHasOptionsMenu(true);
|
||||
//
|
||||
// // Bind the summaries of EditText/List/Dialog/Ringtone preferences
|
||||
// // to their values. When their values change, their summaries are
|
||||
// // updated to reflect the new value, per the Android Design
|
||||
// // guidelines.
|
||||
// bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone"));
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* This fragment shows map preferences only. It is used when the
|
||||
* activity is showing a two-pane settings UI.
|
||||
*/
|
||||
// @TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
// public static class MapPreferenceFragment extends PreferenceFragment {
|
||||
// @Override
|
||||
// public void onCreate(Bundle savedInstanceState) {
|
||||
// super.onCreate(savedInstanceState);
|
||||
// addPreferencesFromResource(R.xml.pref_map);
|
||||
// setHasOptionsMenu(true);
|
||||
//
|
||||
// // Bind the summaries of EditText/List/Dialog/Ringtone preferences
|
||||
// // to their values. When their values change, their summaries are
|
||||
// // updated to reflect the new value, per the Android Design
|
||||
// // guidelines.
|
||||
// bindPreferenceSummaryToValue(findPreference("sync_frequency"));
|
||||
// }
|
||||
// }
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
package values;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import us.keithirwin.tracman.R;
|
||||
|
||||
/**
|
||||
* A simple {@link Fragment} subclass.
|
||||
* Activities that contain this fragment must implement the
|
||||
* {@link AboutFragment.OnBackButtonPressedListener} interface
|
||||
* to handle interaction events.
|
||||
* Use the {@link AboutFragment#newInstance} factory method to
|
||||
* create an instance of this fragment.
|
||||
*/
|
||||
public class AboutFragment extends Fragment {
|
||||
private OnBackButtonPressedListener mListener;
|
||||
|
||||
/**
|
||||
* Use this factory method to create a new instance of
|
||||
* this fragment using the provided parameters.
|
||||
*
|
||||
* @return A new instance of fragment AboutFragment.
|
||||
*/
|
||||
public static AboutFragment newInstance() {
|
||||
AboutFragment fragment = new AboutFragment();
|
||||
return fragment;
|
||||
} public AboutFragment() {}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getActivity().setTitle(R.string.about_name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_about, container, false);
|
||||
|
||||
TextView tv = (TextView) view.findViewById(R.id.about_license);
|
||||
tv.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
|
||||
view.findViewById(R.id.about_back_button).setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
goBack();
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
public void goBack() {
|
||||
if (mListener != null) {
|
||||
mListener.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof OnBackButtonPressedListener) {
|
||||
mListener = (OnBackButtonPressedListener) context;
|
||||
} else {
|
||||
throw new RuntimeException(context.toString()
|
||||
+ " must implement OnFragmentInteractionListener");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This interface must be implemented by activities that contain this
|
||||
* fragment to allow an interaction in this fragment to be communicated
|
||||
* to the activity and potentially other fragments contained in that
|
||||
* activity.
|
||||
* <p/>
|
||||
* See the Android Training lesson <a href=
|
||||
* "http://developer.android.com/training/basics/fragments/communicating.html"
|
||||
* >Communicating with Other Fragments</a> for more information.
|
||||
*/
|
||||
public interface OnBackButtonPressedListener {
|
||||
void goBack();
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
package values;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||