intial commit of 4.0
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @author Twenty Codes
|
||||
* @author ricky barrette
|
||||
*/
|
||||
package com.TwentyCodes.android.FindMyCarLib;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
/**
|
||||
* this is the receiver and handler for alarms that have been previously set by us
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public class AlarmReceiver extends BroadcastReceiver {
|
||||
|
||||
private Context mContext;
|
||||
protected NotificationManager mNotificationManager;
|
||||
private int SIMPLE_NOTFICATION_ID = 0;
|
||||
|
||||
/**
|
||||
* receives a broadcast
|
||||
* when a broadcast is received, we extract the bundle from the intent and then extract the requestCode from the bundle
|
||||
* using the requestCode, we know if it is a notify notification of a time up notification
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
mContext = context;
|
||||
mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
switch(intent.getExtras().getInt("requestCode")){
|
||||
case 0:
|
||||
notifyNotification();
|
||||
break;
|
||||
case 1:
|
||||
timeUpNotification();
|
||||
// mContext.deleteFile("AppService.txt");
|
||||
context.getSharedPreferences(Settings.SETTINGS, 0).edit().remove(Settings.PARKING_TIMER_ALARM).commit();
|
||||
context.getSharedPreferences(Settings.SETTINGS, 0).edit().remove(Settings.PARKING_TIMER_SERVICE).commit();
|
||||
mNotificationManager.cancel(ParkingTimerService.SIMPLE_NOTFICATION_ID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* parking timer is up notification
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private void timeUpNotification(){
|
||||
final Notification notifyDetails = new Notification(R.drawable.show_car_black, mContext.getText(R.string.your_time_up_ticket),System.currentTimeMillis());
|
||||
notifyDetails.defaults |= Notification.DEFAULT_SOUND;
|
||||
notifyDetails.defaults |= Notification.DEFAULT_VIBRATE;
|
||||
notifyDetails.defaults |= Notification.DEFAULT_LIGHTS;
|
||||
notifyDetails.flags |= Notification.FLAG_INSISTENT;
|
||||
notifyDetails.flags |= Notification.FLAG_SHOW_LIGHTS;
|
||||
notifyDetails.flags |= Notification.FLAG_AUTO_CANCEL;
|
||||
Context context = mContext.getApplicationContext();
|
||||
|
||||
Intent startFMC = new Intent(mContext, Main.class);
|
||||
|
||||
PendingIntent intent = PendingIntent.getActivity(mContext, 0, startFMC, android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
notifyDetails.setLatestEventInfo(context, mContext.getText(R.string.your_timer_up_title), mContext.getText(R.string.your_timer_up_msg), intent);
|
||||
|
||||
mNotificationManager.notify(SIMPLE_NOTFICATION_ID, notifyDetails);
|
||||
}
|
||||
|
||||
/**
|
||||
* timer almost up notification
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private void notifyNotification(){
|
||||
final Notification notifyDetails = new Notification(R.drawable.show_car_black,
|
||||
mContext.getText(R.string.your_timer_almost_up_ticket),System.currentTimeMillis());
|
||||
notifyDetails.defaults |= Notification.DEFAULT_SOUND;
|
||||
notifyDetails.defaults |= Notification.DEFAULT_VIBRATE;
|
||||
notifyDetails.defaults |= Notification.DEFAULT_LIGHTS;
|
||||
notifyDetails.flags |= Notification.FLAG_SHOW_LIGHTS;
|
||||
notifyDetails.flags |= Notification.FLAG_AUTO_CANCEL;
|
||||
|
||||
Intent startFMC = new Intent(mContext, Main.class);
|
||||
|
||||
PendingIntent intent = PendingIntent.getActivity(mContext, 0, startFMC, android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
notifyDetails.setLatestEventInfo(mContext, mContext.getText(R.string.your_timer_almost_up_title), mContext.getText(R.string.your_timer_almost_up_msg), intent);
|
||||
|
||||
mNotificationManager.notify(SIMPLE_NOTFICATION_ID, notifyDetails);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* @author Twenty Codes, LLC
|
||||
* @author ricky barrette
|
||||
* @date Sep 22, 2010
|
||||
*/
|
||||
package com.TwentyCodes.android.FindMyCarLib;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Html;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.TwentyCodes.android.FindMyCarLib.UI.DirectionsOverlay;
|
||||
|
||||
/**
|
||||
* this is a custom listview adaptor that wills a listview that has 2 textviews in each row.
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public class DirectionsAdapter extends BaseAdapter {
|
||||
|
||||
private LayoutInflater mInflater;
|
||||
private DirectionsOverlay mDirections;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public DirectionsAdapter(Context context, DirectionsOverlay directions) {
|
||||
mInflater = LayoutInflater.from(context);
|
||||
mDirections = directions;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the size of the main list
|
||||
* @see android.widget.Adapter#getCount()
|
||||
* @return
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mDirections.getDirections().size() + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-Javadoc)
|
||||
* @see android.widget.Adapter#getItem(int)
|
||||
* @param position
|
||||
* @return
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the current position in the list
|
||||
* @see android.widget.Adapter#getItemId(int)
|
||||
* @param position
|
||||
* @return
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
/**
|
||||
* inflates the row from xml, and sets the textviews to their intended vales
|
||||
* @see android.widget.Adapter#getView(int, android.view.View, android.view.ViewGroup)
|
||||
* @param position
|
||||
* @param convertView
|
||||
* @param parent
|
||||
* @return
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
ViewHolder holder;
|
||||
if (convertView == null) {
|
||||
convertView = mInflater.inflate(R.layout.list_row, null);
|
||||
holder = new ViewHolder();
|
||||
holder.text = (TextView) convertView.findViewById(R.id.TextView01);
|
||||
holder.text2 = (TextView) convertView.findViewById(R.id.TextView02);
|
||||
|
||||
convertView.setTag(holder);
|
||||
} else {
|
||||
holder = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the copyrights on the bottom of the directions list
|
||||
*/
|
||||
if (position == mDirections.getDirections().size()){
|
||||
holder.text.setText(mDirections.getCopyrights());
|
||||
holder.text2.setText("");
|
||||
} else {
|
||||
holder.text.setText(Html.fromHtml(mDirections.getDirections().get(position)));
|
||||
holder.text2.setText(mDirections.getDurations().get(position) +" : "+ mDirections.getDistances().get(position));
|
||||
}
|
||||
return convertView;
|
||||
}
|
||||
|
||||
/**
|
||||
* this class will hold the TextViews
|
||||
* @author ricky barrette
|
||||
*/
|
||||
class ViewHolder {
|
||||
TextView text;
|
||||
TextView text2;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* @author Twenty Codes
|
||||
* @author WWPowers
|
||||
* @author ricky barrette
|
||||
*/
|
||||
|
||||
package com.TwentyCodes.android.FindMyCarLib;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
public class FileStream {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
public FileStream(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a boolean that is saved in a file as 1 = true, 0 = false
|
||||
* @param path to file
|
||||
* @return boolean
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public boolean readBoolean(String path){
|
||||
if(readInteger(path) == 1){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns an int, or 0 if the file is not parse-able
|
||||
* @param String path
|
||||
* @return int
|
||||
* @author WWPowers
|
||||
*/
|
||||
public int readInteger(String path) {
|
||||
int data = 0;
|
||||
try {
|
||||
data = Integer.parseInt(readString(path));
|
||||
} catch (NumberFormatException e) {
|
||||
// e.printStackTrace();
|
||||
Log.w(mContext.getClass().getName(),"File "+ path +" did not contain an int");
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* reads a string from a file
|
||||
* @param String path
|
||||
* @return String
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public String readString(String path) {
|
||||
String theLine = "";
|
||||
StringBuffer data = new StringBuffer();
|
||||
int index = 0;
|
||||
try {
|
||||
BufferedReader theReader = new BufferedReader(new InputStreamReader(mContext.openFileInput(path)));
|
||||
while ((theLine = theReader.readLine())!=null) {
|
||||
//if this is not the first line, then append a new line
|
||||
if (index > 0) {
|
||||
data.append("\n");
|
||||
}
|
||||
//append the line from the file to the string
|
||||
data.append(theLine);
|
||||
index++;
|
||||
}
|
||||
} catch (FileNotFoundException e1) {
|
||||
// e1.printStackTrace();
|
||||
Log.w(mContext.getClass().getName(),"File "+ path +" Not Found");
|
||||
} catch(IOException e2) {
|
||||
e2.printStackTrace();
|
||||
}
|
||||
System.gc();
|
||||
String output = data.toString();
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* writes a boolean to a file in the form of an int. 1 = true, 0 = false
|
||||
* @param path to file
|
||||
* @param b boolean to write
|
||||
* @return true if write was successful, false otherwise
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public boolean writeBoolean(String path, boolean b){
|
||||
if (b){
|
||||
return writeString(path, Integer.toString(1));
|
||||
}
|
||||
if (!b){
|
||||
return writeString(path, Integer.toString(0));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* writes an int to a file
|
||||
* @param String path
|
||||
* @param int num
|
||||
* @author WWPowers
|
||||
*/
|
||||
public boolean writeInteger(String path, int num) {
|
||||
return writeString(path, Integer.toString(num));
|
||||
}
|
||||
|
||||
/**
|
||||
* writes a String to a file
|
||||
* @param String path
|
||||
* @param String data
|
||||
* @return boolean true if write was a success
|
||||
* @return boolean false if write was not a success
|
||||
* @author ricky barrette
|
||||
*/
|
||||
protected boolean writeString(String path, String data) {
|
||||
try {
|
||||
FileOutputStream theFile = mContext.openFileOutput(path, Context.MODE_PRIVATE);
|
||||
theFile.write(data.getBytes());
|
||||
theFile.flush();
|
||||
theFile.close();
|
||||
} catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
541
FindMyCarLib/src/com/TwentyCodes/android/FindMyCarLib/Main.java
Normal file
541
FindMyCarLib/src/com/TwentyCodes/android/FindMyCarLib/Main.java
Normal file
@@ -0,0 +1,541 @@
|
||||
/**
|
||||
* Main.java
|
||||
* @date Nov 14, 2011
|
||||
* @author ricky barrette
|
||||
* @author Twenty Codes, LLC
|
||||
*/
|
||||
package com.TwentyCodes.android.FindMyCarLib;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.location.LocationManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.PowerManager;
|
||||
import android.os.PowerManager.WakeLock;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
import com.TwentyCodes.android.FindMyCarLib.UI.CustomViewPager;
|
||||
import com.TwentyCodes.android.FindMyCarLib.UI.DirectionsOverlay;
|
||||
import com.TwentyCodes.android.FindMyCarLib.UI.fragments.DirectionsListFragment;
|
||||
import com.TwentyCodes.android.FindMyCarLib.UI.fragments.DirectionsListFragment.OnDirectionSelectedListener;
|
||||
import com.TwentyCodes.android.FindMyCarLib.UI.fragments.MapFragment;
|
||||
import com.TwentyCodes.android.FindMyCarLib.UI.fragments.MapFragment.MapFragmentListener;
|
||||
import com.TwentyCodes.android.FindMyCarLib.UI.fragments.NotesFragment;
|
||||
import com.TwentyCodes.android.FindMyCarLib.debug.Debug;
|
||||
import com.TwentyCodes.android.SkyHook.SkyHookRegistration;
|
||||
import com.TwentyCodes.android.exception.ExceptionHandler;
|
||||
import com.google.ads.AdRequest;
|
||||
import com.google.ads.AdView;
|
||||
import com.google.android.maps.GeoPoint;
|
||||
import com.jakewharton.android.viewpagerindicator.TitlePageIndicator;
|
||||
import com.jakewharton.android.viewpagerindicator.TitledFragmentAdapter;
|
||||
import com.skyhookwireless.wps.RegistrationCallback;
|
||||
import com.skyhookwireless.wps.WPSContinuation;
|
||||
import com.skyhookwireless.wps.WPSReturnCode;
|
||||
|
||||
/**
|
||||
* This is the Main Activity of FMC Full & Lite
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public class Main extends FragmentActivity implements RegistrationCallback, MapFragmentListener, OnDirectionSelectedListener {
|
||||
|
||||
private static final String SPLASH = "splash";
|
||||
private static final String TAG = "Main";
|
||||
private SharedPreferences mSettings;
|
||||
private Dialog mSplashDialog;
|
||||
private WakeLock mWakeLock;
|
||||
private MapFragment mMap;
|
||||
private NotesFragment mNotes;
|
||||
private CustomViewPager mPager;
|
||||
private ArrayList<Fragment> mFragments;
|
||||
private DirectionsListFragment mDirectionsFragment;
|
||||
private TitlePageIndicator mIndicator;
|
||||
public static boolean isFull = true;
|
||||
|
||||
/**
|
||||
* displays a dialog to inform that the gps is disabled and provides them with a shortcut to the settings page
|
||||
* To make this dialog more friendly, i have removed the No button (to mimic google maps) and have enabled the dialog to be cancel via the back button.
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public static void enableGPSdialog(final Context context) {
|
||||
new AlertDialog.Builder(context)
|
||||
.setMessage(R.string.gps_is_disabled).setCancelable(true)
|
||||
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
Intent callGPSSettingIntent = new Intent( android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
|
||||
context.startActivity(callGPSSettingIntent);
|
||||
dialog.cancel();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
/**
|
||||
* displays a dialog informing user that this feature is found only in full
|
||||
*/
|
||||
public static void featureInFullDialog(final Context context) {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(R.string.feature_in_fmc_full)
|
||||
.setMessage(R.string.feature_in_fmc_full_description)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse("market://search?q=pname:com.TwentyCodes.android.FindMyCarFull"));
|
||||
context.startActivity(intent);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
//do nothing
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* displays the welcome dialog
|
||||
*
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private void displayWelcomeDialog() {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(getText(R.string.welcome))
|
||||
.setMessage(R.string.welcome_msg).setCancelable(false)
|
||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
mSettings.edit().putBoolean(Settings.FIRST_BOOT, true).commit();
|
||||
|
||||
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
||||
|
||||
/*
|
||||
* if the gps provider is disabled, then ask user if they want to enable it
|
||||
* else display the gps progress
|
||||
*/
|
||||
if(!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER))
|
||||
enableGPSdialog(Main.this);
|
||||
else
|
||||
mMap.setGPSDialogEnabled(true);
|
||||
|
||||
/*
|
||||
* the map is no longer needed, clear it from memory
|
||||
*/
|
||||
mMap = null;
|
||||
}
|
||||
})
|
||||
.show();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-Javadoc)
|
||||
* @see com.skyhookwireless.wps._sdkjc#done()
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public void done() {
|
||||
// UNUSED
|
||||
}
|
||||
|
||||
/**
|
||||
* displays lic dialog and welcome to find my car dialog
|
||||
*/
|
||||
public void eulaAlert (){
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.eula)
|
||||
.setMessage(R.string.eulaagreement).setCancelable(false)
|
||||
.setPositiveButton(R.string.accept, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
mSettings.edit().putBoolean(Settings.ACCEPTED, true).commit();
|
||||
update();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.decline, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
)
|
||||
.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-Javadoc)
|
||||
* @see com.skyhookwireless.wps._sdkjc#handleError(com.skyhookwireless.wps.WPSReturnCode)
|
||||
* @param arg0
|
||||
* @return
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public WPSContinuation handleError(WPSReturnCode arg0) {
|
||||
|
||||
Log.e(TAG,"there was an error regestering you "+ arg0.toString());
|
||||
return WPSContinuation.WPS_CONTINUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* called when skyhook successfully registers a new user.
|
||||
* @see com.skyhookwireless.wps.RegistrationCallback#handleSuccess()
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public void handleSuccess() {
|
||||
Log.d(TAG,"successfully registered new user");
|
||||
mSettings.edit().putBoolean(Settings.IS_REGISTERED, true).commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler(this));
|
||||
setContentView(R.layout.main);
|
||||
|
||||
mSettings = this.getSharedPreferences(Settings.SETTINGS, 0);
|
||||
mMap = new MapFragment();
|
||||
mMap.setMapFragmentListener(this);
|
||||
|
||||
//only display ads if this is the lite version
|
||||
if(!isFull){
|
||||
AdView ad = (AdView) findViewById(R.id.ad);
|
||||
ad.setVisibility(View.VISIBLE);
|
||||
ad.loadAd(new AdRequest());
|
||||
}
|
||||
|
||||
if(icicle != null){
|
||||
if (icicle.containsKey(SPLASH))
|
||||
// Show splash screen if still loading
|
||||
if (icicle.getBoolean(SPLASH)) {
|
||||
showSplashScreen();
|
||||
}
|
||||
|
||||
// Rebuild your UI with your saved state here
|
||||
} else {
|
||||
showSplashScreen();
|
||||
// Do your heavy loading here on a background thread
|
||||
new Thread( new Runnable(){
|
||||
@Override
|
||||
public void run(){
|
||||
//registers user with skyhook
|
||||
new SkyHookRegistration(Main.this).registerNewUser(Main.this);
|
||||
|
||||
}
|
||||
}).start();
|
||||
|
||||
//remove notification from notification bar
|
||||
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
notificationManager.cancel(0);
|
||||
|
||||
}
|
||||
|
||||
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
|
||||
mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
|
||||
|
||||
/*
|
||||
* Page titles
|
||||
*/
|
||||
String[] titles = new String[]{
|
||||
getString(R.string.directions),
|
||||
getString(R.string.map),
|
||||
getString(R.string.notes)
|
||||
};
|
||||
|
||||
/*
|
||||
* page icons
|
||||
*/
|
||||
int[] icons = new int[]{
|
||||
R.drawable.nav_action_bar,
|
||||
R.drawable.map_action_bar,
|
||||
R.drawable.notes_action_bar
|
||||
};
|
||||
|
||||
mFragments = new ArrayList<Fragment>();
|
||||
|
||||
mDirectionsFragment = new DirectionsListFragment(this);
|
||||
mFragments.add(mDirectionsFragment);
|
||||
mFragments.add(mMap);
|
||||
mNotes = new NotesFragment();
|
||||
mFragments.add(mNotes);
|
||||
|
||||
//Populate the pager
|
||||
mPager = (CustomViewPager) findViewById(R.id.pager);
|
||||
|
||||
if(mPager != null)
|
||||
mPager.setAdapter(new TitledFragmentAdapter(this.getSupportFragmentManager(), mFragments, titles, icons));
|
||||
|
||||
//populate the pager's indicator
|
||||
mIndicator = (TitlePageIndicator)findViewById(R.id.indicator);
|
||||
if(mIndicator != null)
|
||||
mIndicator.setViewPager(mPager);
|
||||
|
||||
mPager.setCurrentItem(1);
|
||||
mIndicator.setCurrentItem(1);
|
||||
mPager.setPagingEnabled(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* called when the activity is going to be paused
|
||||
* we stop all location based services and release the wake lock if there is one
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
protected void onPause() {
|
||||
Log.i(TAG,"onPause()");
|
||||
|
||||
//remove wake lock if it is enabled
|
||||
if(mWakeLock.isHeld())
|
||||
mWakeLock.release();
|
||||
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
Log.i(TAG,"onResume()");
|
||||
|
||||
//set stay awake to preference
|
||||
if(mSettings.getBoolean(Settings.STAY_AWAKE, false)){
|
||||
if(!mWakeLock.isHeld()){
|
||||
mWakeLock.acquire();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-Javadoc)
|
||||
* @see android.support.v4.app.FragmentActivity#onSaveInstanceState(android.os.Bundle)
|
||||
*/
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle icicle) {
|
||||
if(mSplashDialog != null)
|
||||
icicle.getBoolean(SPLASH, mSplashDialog.isShowing());
|
||||
super.onSaveInstanceState(icicle);
|
||||
}
|
||||
|
||||
/**
|
||||
* called when activity is stopped. lifecycle method.
|
||||
* @author wwpowers
|
||||
*/
|
||||
@Override
|
||||
protected void onStop() {
|
||||
Log.i(TAG,"onStop()");
|
||||
|
||||
if(mWakeLock.isHeld()){
|
||||
mWakeLock.release();
|
||||
}
|
||||
|
||||
super.onStop();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* parses all old save files from 2.0.6b34 and older into shared_prefs file settings.xml . it will parse the following files
|
||||
* AppLat.txt ,
|
||||
* AppLon.txt ,
|
||||
* FirstBoot.txt ,
|
||||
* StayAwake.txt ,
|
||||
* Notes.txt ,
|
||||
* Address.txt ,
|
||||
* AppUnit.txt , &
|
||||
* AppService.txt
|
||||
* @return true if files were committed successful to shared_prefs settings.xml
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private boolean parseOldSaveFilesToSharedPrefs(){
|
||||
/*
|
||||
* get the file stream class to read all old files
|
||||
* and get the editor for shared_prefs settings.xml
|
||||
*/
|
||||
FileStream fs = new FileStream(this);
|
||||
Editor editor = mSettings.edit();
|
||||
|
||||
/*
|
||||
* parse in the old files and save them
|
||||
*/
|
||||
editor.putInt(Settings.LAT, fs.readInteger("AppLat.txt"));
|
||||
editor.putInt(Settings.LON, fs.readInteger("AppLon.txt"));
|
||||
editor.putBoolean(Settings.STAY_AWAKE, fs.readBoolean("StayAwake.txt"));
|
||||
|
||||
if(fs.readBoolean("AppUnit.txt")){
|
||||
editor.putString(Settings.MEASUREMENT_UNIT, "Metric");
|
||||
} else {
|
||||
editor.putString(Settings.MEASUREMENT_UNIT, "Standard");
|
||||
}
|
||||
|
||||
editor.putBoolean(Settings.PARKING_TIMER_ALARM, fs.readBoolean("AppService.txt"));
|
||||
editor.putBoolean(Settings.FIRST_BOOT, fs.readBoolean("FirstBoot.txt"));
|
||||
editor.putString(Settings.NOTE, fs.readString("Notes.txt"));
|
||||
editor.putString(Settings.ADDRESS, fs.readString("Address.txt"));
|
||||
|
||||
/*
|
||||
* remove old files
|
||||
*/
|
||||
deleteFile("AppLat.txt");
|
||||
deleteFile("AppLon.txt");
|
||||
deleteFile("StayAwake.txt");
|
||||
deleteFile("AppUnit.txt");
|
||||
deleteFile("AppService.txt");
|
||||
deleteFile("FirstBoot.txt");
|
||||
deleteFile("Notes.txt");
|
||||
deleteFile("Address.txt");
|
||||
|
||||
/*
|
||||
* commit the changes
|
||||
*/
|
||||
return editor.commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* displays a quit dialog
|
||||
* @since 0.0.2
|
||||
* @author ricky barrette 3-30-2010
|
||||
*/
|
||||
public void quitDialog(){
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setMessage(getText(R.string.quit_dialog)).setCancelable(false)
|
||||
.setPositiveButton(getText(R.string.yes), new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(getText(R.string.no), new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the Dialog that displays the splash screen
|
||||
*/
|
||||
protected void removeSplashScreen() {
|
||||
if (mSplashDialog != null) {
|
||||
mSplashDialog.dismiss();
|
||||
mSplashDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shows the splash screen over the full Activity
|
||||
*/
|
||||
protected void showSplashScreen() {
|
||||
mMap.setGPSDialogEnabled(false);
|
||||
mSplashDialog = new Dialog(this, android.R.style.Theme_Translucent_NoTitleBar_Fullscreen);
|
||||
mSplashDialog.setContentView(R.layout.powered_by_skyhook);
|
||||
mSplashDialog.setCancelable(false);
|
||||
mSplashDialog.show();
|
||||
|
||||
// Set Runnable to remove splash screen just in case
|
||||
final Handler handler = new Handler();
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
removeSplashScreen();
|
||||
|
||||
//loads first boot dialog if this is the first boot
|
||||
if (! mSettings.getBoolean(Settings.ACCEPTED, false) || Debug.FORCE_FIRSTBOOT_DIALOG)
|
||||
eulaAlert();
|
||||
else
|
||||
update();
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
/**
|
||||
* check to see if there was an update installed. if the update needs to do any upgrades, it will be done here
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private void update() {
|
||||
|
||||
/*
|
||||
* get build number and compare to saved build number, then check to see if there is something we need to do
|
||||
*/
|
||||
try {
|
||||
int build_number = this.getPackageManager().getPackageInfo(this.getPackageName(), 0).versionCode;
|
||||
|
||||
/*
|
||||
* if there is no build number saved && there is a FirstBoot.txt file then update old save file system to shared_prefs
|
||||
*/
|
||||
if(mSettings.getInt(Settings.BUILD_NUMBER, 0) == 0 && new FileStream(this).readBoolean("FirstBoot.txt")){
|
||||
Log.v(TAG, "updateding save files to shared_prefs");
|
||||
parseOldSaveFilesToSharedPrefs();
|
||||
}
|
||||
|
||||
/*
|
||||
* if this is the first time running this build display welcome dialog
|
||||
*/
|
||||
if(mSettings.getInt(Settings.BUILD_NUMBER, 0) < build_number || Debug.FORCE_FIRSTBOOT_DIALOG){
|
||||
displayWelcomeDialog();
|
||||
} else {
|
||||
mMap.setGPSDialogEnabled(true);
|
||||
}
|
||||
|
||||
mSettings.edit().putInt(Settings.BUILD_NUMBER, build_number).commit();
|
||||
} catch (NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called when a car is deleted
|
||||
* (non-Javadoc)
|
||||
* @see com.TwentyCodes.android.FindMyCarLib.UI.fragments.MapFragment.MapFragmentListener#onCarDeleted()
|
||||
*/
|
||||
@Override
|
||||
public void onCarDeleted() {
|
||||
mNotes.delete();
|
||||
mDirectionsFragment.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* called when directions are displayed
|
||||
* (non-Javadoc)
|
||||
* @see com.TwentyCodes.android.FindMyCarLib.UI.fragments.MapFragment.MapFragmentListener#onDirectionsDisplayed(java.util.ArrayList, java.util.ArrayList, java.util.ArrayList, java.util.ArrayList, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void onDirectionsDisplayed(final DirectionsOverlay directions) {
|
||||
this.runOnUiThread(new Runnable(){
|
||||
@Override
|
||||
public void run(){
|
||||
mDirectionsFragment.setDirections(directions);
|
||||
mPager.setCurrentItem(2);
|
||||
|
||||
mPager.setCurrentItem(0);
|
||||
mIndicator.setCurrentItem(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* called when a direction is selected
|
||||
* (non-Javadoc)
|
||||
* @see com.TwentyCodes.android.FindMyCarLib.UI.fragments.DirectionsListFragment.OnDirectionSelectedListener#onDirectionSelected(com.google.android.maps.GeoPoint)
|
||||
*/
|
||||
@Override
|
||||
public void onDirectionSelected(GeoPoint point) {
|
||||
if(mMap != null) {
|
||||
mMap.panToGeoPoint(point, true);
|
||||
mPager.setCurrentItem(1);
|
||||
mIndicator.setCurrentItem(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* ParkignTimerActivity.java
|
||||
* @date Dec 4, 2011
|
||||
* @author ricky barrette
|
||||
* @author Twenty Codes, LLC
|
||||
*/
|
||||
package com.TwentyCodes.android.FindMyCarLib;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
/**
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public class ParkignTimerActivity extends FragmentActivity {
|
||||
|
||||
/**
|
||||
* (non-Javadoc)
|
||||
* @see android.support.v4.app.FragmentActivity#onCreate(android.os.Bundle)
|
||||
*/
|
||||
@Override
|
||||
protected void onCreate(Bundle arg0) {
|
||||
super.onCreate(arg0);
|
||||
this.setContentView(R.layout.parking_timer_activity);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/**
|
||||
* @author Twenty Codes
|
||||
* @author wwpowers
|
||||
* @author ricky barrette
|
||||
*/
|
||||
package com.TwentyCodes.android.FindMyCarLib;
|
||||
|
||||
import com.TwentyCodes.android.FindMyCarLib.UI.fragments.MapFragment;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.CountDownTimer;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
|
||||
public class ParkingTimerService extends Service {
|
||||
|
||||
private static final String TAG = "ParkingTimerService";
|
||||
private long mMinutes;
|
||||
protected Context mContext = this;
|
||||
protected NotificationManager mNotificationManager;
|
||||
public static int SIMPLE_NOTFICATION_ID = 656;
|
||||
public PendingIntent intent;
|
||||
private static long mPeriod = 60000L; //this is the period in which the notification will be updated.
|
||||
private int mImage; //value for notification image
|
||||
private Timer mTimer;
|
||||
|
||||
/**
|
||||
* auto generated, not used
|
||||
* (non-Javadoc)
|
||||
* @see android.app.Service#onBind(android.content.Intent)
|
||||
* @param arg0
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public IBinder onBind(Intent arg0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* auto generated, not used
|
||||
* (non-Javadoc)
|
||||
* @see android.app.Service#onCreate()
|
||||
*/
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
/**
|
||||
* if the service stops, remove the notification
|
||||
* (non-Javadoc)
|
||||
* @see android.app.Service#onDestroy()
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public void onDestroy(){
|
||||
mNotificationManager.cancel(656);
|
||||
mTimer.cancel();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called when startService is called. only used in 2.x android.
|
||||
* @author wwpowers
|
||||
*/
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
Log.i(TAG, "onStartCommand.Service started with start id of: " + startId);
|
||||
getData(intent);
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
/**
|
||||
* To keep backwards compatibility we override onStart which is the equivalent of onStartCommand in pre android 2.x
|
||||
* @author wwpowers
|
||||
*/
|
||||
@Override
|
||||
public void onStart(Intent intent, int startId) {
|
||||
Log.i(TAG, "onStart.Service started with start id of: " + startId);
|
||||
getData(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* extracts time in milliseconds from from a bundle that was packaged into the intent, and starts the Timer()
|
||||
* @param intent
|
||||
* @author wwpowers
|
||||
*/
|
||||
private void getData(Intent intent) {
|
||||
Bundle bundle = intent.getBundleExtra("minutes");
|
||||
//set the update interval to update the ongoing parking timer notification
|
||||
mPeriod = Integer.parseInt(getSharedPreferences(Settings.SETTINGS, 0).getString(Settings.PARKING_TIMER_UPDATE_INTERVAL, "60"));
|
||||
mMinutes = bundle.getLong("minutes");
|
||||
mImage = bundle.getInt("color");
|
||||
mTimer = new Timer(mMinutes);
|
||||
mTimer.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* this internal class will handle all timing functions of this service.
|
||||
* when the Timer() is started it will create an ongoing notification the display how much time is left for the parking timer,
|
||||
* and will update every mPeriod milliseconds
|
||||
* @author ricky barrette
|
||||
*/
|
||||
class Timer extends CountDownTimer {
|
||||
|
||||
private Notification mNotifyDetails;
|
||||
|
||||
/**
|
||||
* creates a new Timer that creates an ongoing notification the display how much time is left for the parking timer,
|
||||
* and will update every mPeriod milliseconds
|
||||
* @param millisInFuture
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public Timer(long millisInFuture) {
|
||||
super(millisInFuture, mPeriod);
|
||||
mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
mNotifyDetails = new Notification(mImage, stringTime(millisInFuture) , System.currentTimeMillis());
|
||||
mNotifyDetails.flags |= Notification.FLAG_ONGOING_EVENT;
|
||||
Intent startFMC = new Intent(mContext, MapFragment.class);
|
||||
intent = PendingIntent.getActivity(mContext, 0, startFMC, android.content.Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
mNotifyDetails.setLatestEventInfo(mContext, "Parking Timer", stringTime(millisInFuture), intent);
|
||||
mNotificationManager.notify(SIMPLE_NOTFICATION_ID, mNotifyDetails);
|
||||
}
|
||||
|
||||
/**
|
||||
* removes the ongoing parking timer notification and kills the service
|
||||
* (non-Javadoc)
|
||||
* @see android.os.CountDownTimer#onFinish()
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public void onFinish() {
|
||||
mNotificationManager.cancel(656);
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* updates the ongoing Parking Timer notification
|
||||
* (non-Javadoc)
|
||||
* @see android.os.CountDownTimer#onTick(long)
|
||||
* @param millisUntilFinished
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public void onTick(long millisUntilFinished) {
|
||||
mMinutes = millisUntilFinished;
|
||||
|
||||
mNotifyDetails.setLatestEventInfo(mContext, "Parking Timer", stringTime(millisUntilFinished), intent);
|
||||
mNotificationManager.notify(SIMPLE_NOTFICATION_ID, mNotifyDetails);
|
||||
}
|
||||
|
||||
/**
|
||||
* convince method for formating milliseconds into hour : minutes format
|
||||
* @param mills
|
||||
* @return human readable hour : minutes format
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private String stringTime(long mills){
|
||||
int hours = (int) (mills / 3600000);
|
||||
mills = mills - (hours * 3600000);
|
||||
int minutes = (int) ( mills / 60000);
|
||||
int seconds = (int) (mills % 60000);
|
||||
seconds = seconds / 1000;
|
||||
return hours +" : "+ padTime(minutes) +" : "+ padTime(seconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* convince method for formating the seconds string
|
||||
* @param seconds
|
||||
* @return formated string
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private String padTime(int time){
|
||||
if (time <= 9)
|
||||
return "0"+ time;
|
||||
return ""+ time;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* @author Twenty Codes
|
||||
* @author ricky barrette
|
||||
*/
|
||||
package com.TwentyCodes.android.FindMyCarLib;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceChangeListener;
|
||||
import android.preference.PreferenceActivity;
|
||||
|
||||
/**
|
||||
* @author ricky barrette
|
||||
*
|
||||
*/
|
||||
public class Settings extends PreferenceActivity implements OnPreferenceChangeListener {
|
||||
|
||||
/*
|
||||
* the following strings are for the shared_prefs settings.xml
|
||||
*/
|
||||
public final static String SETTINGS = "settings";
|
||||
public final static String LAT = "lat";
|
||||
public final static String LON = "lon";
|
||||
public final static String MEASUREMENT_UNIT = "measurement_unit";
|
||||
public final static String LAYERS = "layers";
|
||||
public final static String STAY_AWAKE = "stay_awake";
|
||||
public final static String FIRST_BOOT = "first_boot";
|
||||
public final static String ADDRESS = "address";
|
||||
public final static String NOTE = "note";
|
||||
public final static String PARKING_TIMER_ALARM = "parking_timer_alarm";
|
||||
public final static String BUILD_NUMBER = "build_number";
|
||||
public final static String PARKING_TIMER_SERVICE = "parking_timer_service";
|
||||
public final static String PARKING_TIMER_UPDATE_INTERVAL ="parking_timer_update_interval";
|
||||
public final static String PARKING_TIMER_ONGOING_NOTIFICATION_ISENABLED ="parking_timer_ongoing_notification_isenabled";
|
||||
public final static String PARKING_TIMER_NOTIFICATION_COLOR = "parking_timer_notification_color";
|
||||
public final static String DIRECTIONS = "directions";
|
||||
public final static String IS_REGISTERED = "is_registered";
|
||||
public final static String COMPASS_OPTION = "compass_option";
|
||||
// private final static String TAG = "Settings";
|
||||
protected static final String ACCEPTED = "accepted";
|
||||
|
||||
/**
|
||||
*
|
||||
* (non-Javadoc)
|
||||
* @see android.preference.PreferenceActivity#onCreate(android.os.Bundle)
|
||||
* @param icicle
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle icicle){
|
||||
super.onCreate(icicle);
|
||||
|
||||
//set shared_prefs name
|
||||
getPreferenceManager().setSharedPreferencesName(SETTINGS);
|
||||
|
||||
//load preferences xml. this load relies on only wether the app is full or not. it will show the check license option if full and leave it out if lite
|
||||
addPreferencesFromResource(R.xml.settings);
|
||||
|
||||
findPreference(MEASUREMENT_UNIT).setOnPreferenceChangeListener(this);
|
||||
// findPreference(PARKING_TIMER_UPDATE_INTERVAL).setOnPreferenceChangeListener(this);
|
||||
|
||||
// if(Main.isFull){
|
||||
// findPreference(PARKING_TIMER_ONGOING_NOTIFICATION_ISENABLED).setEnabled(true);
|
||||
// findPreference(PARKING_TIMER_UPDATE_INTERVAL).setOnPreferenceChangeListener(this);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
SharedPreferences shared_prefs = getPreferenceManager().getSharedPreferences();
|
||||
findPreference(MEASUREMENT_UNIT).setSummary(shared_prefs.getString(MEASUREMENT_UNIT, "Metric"));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-Javadoc)
|
||||
* @see android.preference.Preference.OnPreferenceChangeListener#onPreferenceChange(android.preference.Preference, java.lang.Object)
|
||||
* @param preference
|
||||
* @param newValue
|
||||
* @return
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
String key = preference.getKey();
|
||||
|
||||
if(key.equals(MEASUREMENT_UNIT)){
|
||||
preference.setSummary(newValue.toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
// if(key.equalsIgnoreCase(PARKING_TIMER_UPDATE_INTERVAL)){
|
||||
// try {
|
||||
// Integer.parseInt(newValue.toString());
|
||||
// Toast.makeText(this, getText(R.string.update_interval_updated_to)+ " " + newValue.toString() + " " + getText(R.string.seconds), Toast.LENGTH_LONG).show();
|
||||
// } catch (NumberFormatException e) {
|
||||
// e.printStackTrace();
|
||||
// Toast.makeText(this, getText(R.string.the_vaule_was_not_a_number_update_interval_60), Toast.LENGTH_LONG).show();
|
||||
// getPreferenceManager().getSharedPreferences().edit().putInt(PARKING_TIMER_UPDATE_INTERVAL, 60);
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* ViewPager.java
|
||||
* @date Nov 25, 2011
|
||||
* @author ricky barrette
|
||||
* @author Twenty Codes, LLC
|
||||
*/
|
||||
package com.TwentyCodes.android.FindMyCarLib.UI;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
/**
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public class CustomViewPager extends ViewPager {
|
||||
|
||||
private boolean enabled;
|
||||
|
||||
public CustomViewPager(Context arg0) {
|
||||
super(arg0);
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
public CustomViewPager(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
if (this.enabled) {
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent event) {
|
||||
if (this.enabled) {
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setPagingEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
/**
|
||||
* DirectionsOverlay.java
|
||||
* @date Nov 10, 2011
|
||||
* @author ricky barrette
|
||||
* @author Twenty Codes, LLC
|
||||
*/
|
||||
package com.TwentyCodes.android.FindMyCarLib.UI;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.util.Log;
|
||||
|
||||
import com.TwentyCodes.android.FindMyCarLib.debug.Debug;
|
||||
import com.TwentyCodes.android.location.MapView;
|
||||
import com.google.android.maps.GeoPoint;
|
||||
|
||||
/**
|
||||
* This Overlay class will be used to display provided by the Google Directions API on a map
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public class DirectionsOverlay {
|
||||
|
||||
private static final String TAG = "DirectionsOverlay";
|
||||
ArrayList<PathOverlay> mPath;
|
||||
ArrayList<String> mDirections;
|
||||
private MapView mMapView;
|
||||
private OnDirectionsCompleteListener mListener;
|
||||
private String mCopyRights;
|
||||
private ArrayList<GeoPoint> mPoints;
|
||||
private ArrayList<String> mDistance;
|
||||
private ArrayList<String> mDuration;
|
||||
private ArrayList<String> mWarnings;
|
||||
|
||||
/**
|
||||
* Downloads and Creates a new DirectionsOverlay from the provided points
|
||||
* @param origin point
|
||||
* @param destination point
|
||||
* @author ricky barrette
|
||||
* @throws IOException
|
||||
* @throws ClientProtocolException
|
||||
* @throws IllegalStateException
|
||||
* @throws JSONException
|
||||
*/
|
||||
public DirectionsOverlay(MapView map, GeoPoint origin, GeoPoint destination, OnDirectionsCompleteListener listener) throws IllegalStateException, ClientProtocolException, IOException, JSONException {
|
||||
mMapView = map;
|
||||
mListener = listener;
|
||||
String json = downloadJSON(generateUrl(origin, destination));
|
||||
drawPath(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new DirectionsOverlay from the provided String JSON
|
||||
* @param json
|
||||
* @throws JSONException
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public DirectionsOverlay(MapView map, String json, OnDirectionsCompleteListener listener) throws JSONException{
|
||||
mListener = listener;
|
||||
mMapView = map;
|
||||
drawPath(json);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads Google Directions JSON from the Internet
|
||||
* @param url
|
||||
* @return
|
||||
* @throws IllegalStateException
|
||||
* @throws ClientProtocolException
|
||||
* @throws IOException
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private String downloadJSON(String url) throws IllegalStateException, ClientProtocolException, IOException {
|
||||
if(Debug.DEBUG)
|
||||
Log.d(TAG, url);
|
||||
if(url == null)
|
||||
throw new NullPointerException();
|
||||
StringBuffer response = new StringBuffer();
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(new DefaultHttpClient().execute(new HttpGet(url)).getEntity().getContent()));
|
||||
String buff = null;
|
||||
while ((buff = br.readLine()) != null)
|
||||
response.append(buff);
|
||||
return response.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new DirectionsOverlay from the json provided
|
||||
* @param json of Google Directions API
|
||||
* @author ricky barrette
|
||||
* @return
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void drawPath(String json) throws JSONException{
|
||||
if(Debug.DEBUG){
|
||||
Log.d(TAG, "drawPath");
|
||||
Log.d(TAG, json);
|
||||
}
|
||||
mPath = new ArrayList<PathOverlay>();
|
||||
mDirections = new ArrayList<String>();
|
||||
mPoints = new ArrayList<GeoPoint>();
|
||||
mDistance = new ArrayList<String>();
|
||||
mDuration = new ArrayList<String>();
|
||||
|
||||
//get first route
|
||||
JSONObject route = new JSONObject(json).getJSONArray("routes").getJSONObject(0);
|
||||
|
||||
mCopyRights = route.getString("copyrights");
|
||||
//route.getString("status");
|
||||
|
||||
JSONObject leg = route.getJSONArray("legs").getJSONObject(0);
|
||||
getDistance(leg);
|
||||
getDuration(leg);
|
||||
// mMapView.getOverlays().add(new PathOverlay(getGeoPoint(leg.getJSONObject("start_location")), 12, Color.GREEN));
|
||||
// mMapView.getOverlays().add(new PathOverlay(getGeoPoint(leg.getJSONObject("end_location")), 12, Color.RED));
|
||||
|
||||
leg.getString("start_address");
|
||||
leg.getString("end_address");
|
||||
|
||||
// JSONArray warnings = leg.getJSONArray("warnings");
|
||||
// for(int i = 0; i < warnings.length(); i++){
|
||||
// mWarnings.add(warnings.get)w
|
||||
// }w
|
||||
|
||||
/*
|
||||
* here we will parse the steps of the directions
|
||||
*/
|
||||
if(Debug.DEBUG)
|
||||
Log.d(TAG, "processing steps");
|
||||
JSONArray steps = leg.getJSONArray("steps");
|
||||
JSONObject step = null;
|
||||
for(int i = 0; i < steps.length(); i++){
|
||||
if(Debug.DEBUG)
|
||||
Log.d(TAG, "step "+i);
|
||||
|
||||
step = steps.getJSONObject(i);
|
||||
|
||||
if(Debug.DEBUG){
|
||||
Log.d(TAG, "start "+getGeoPoint(step.getJSONObject("start_location")).toString());
|
||||
Log.d(TAG, "end "+getGeoPoint(step.getJSONObject("end_location")).toString());
|
||||
}
|
||||
|
||||
// if(Debug.DEBUG)
|
||||
// mMapView.getOverlays().add(new PathOverlay(getGeoPoint(step.getJSONObject("start_location")), getGeoPoint(step.getJSONObject("end_location")), Color.MAGENTA));
|
||||
|
||||
decodePoly(step);
|
||||
|
||||
mDuration.add(getDuration(step));
|
||||
|
||||
mDistance.add(getDistance(step));
|
||||
|
||||
mDirections.add(step.getString("html_instructions"));
|
||||
// Log.d("TEST", step.getString("html_instructions"));
|
||||
mPoints.add(getGeoPoint(step.getJSONObject("start_location")));
|
||||
|
||||
}
|
||||
if(Debug.DEBUG)
|
||||
Log.d(TAG, "finished parsing");
|
||||
|
||||
if(mMapView != null){
|
||||
mMapView.getOverlays().addAll(mPath);
|
||||
mMapView.postInvalidate();
|
||||
}
|
||||
|
||||
if(mListener != null)
|
||||
mListener.onDirectionsComplete(DirectionsOverlay.this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param origin
|
||||
* @param destination
|
||||
* @return The Google API url for our directions
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private String generateUrl(GeoPoint origin, GeoPoint destination){
|
||||
return "http://maps.googleapis.com/maps/api/directions/json?&origin="+
|
||||
Double.toString(origin.getLatitudeE6() / 1.0E6)+
|
||||
","+
|
||||
Double.toString(origin.getLongitudeE6() / 1.0E6)+
|
||||
"&destination="+
|
||||
Double.toString(destination.getLatitudeE6() / 1.0E6)+
|
||||
","+
|
||||
Double.toString(destination.getLongitudeE6() / 1.0E6)+
|
||||
"&sensor=true&mode=walking";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deocodes googles polyline
|
||||
* @param encoded
|
||||
* @return a list of geopoints representing the path
|
||||
* @author Mark McClure http://facstaff.unca.edu/mcmcclur/googlemaps/encodepolyline/
|
||||
* @author ricky barrette
|
||||
* @throws JSONException
|
||||
*/
|
||||
private void decodePoly(JSONObject step) throws JSONException {
|
||||
if(Debug.DEBUG)
|
||||
Log.d(TAG, "decodePoly");
|
||||
|
||||
String encoded = step.getJSONObject("polyline").getString("points");
|
||||
int index = 0, len = encoded.length();
|
||||
int lat = 0, lng = 0;
|
||||
|
||||
GeoPoint last = null;
|
||||
while (index < len) {
|
||||
int b, shift = 0, result = 0;
|
||||
do {
|
||||
b = encoded.charAt(index++) - 63;
|
||||
result |= (b & 0x1f) << shift;
|
||||
shift += 5;
|
||||
} while (b >= 0x20);
|
||||
int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
|
||||
lat += dlat;
|
||||
|
||||
shift = 0;
|
||||
result = 0;
|
||||
do {
|
||||
b = encoded.charAt(index++) - 63;
|
||||
result |= (b & 0x1f) << shift;
|
||||
shift += 5;
|
||||
} while (b >= 0x20);
|
||||
int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
|
||||
lng += dlng;
|
||||
|
||||
GeoPoint p = new GeoPoint((int) (((double) lat / 1E5) * 1E6), (int) (((double) lng / 1E5) * 1E6));
|
||||
|
||||
if(Debug.DEBUG){
|
||||
Log.d(TAG, "current = "+ p.toString());
|
||||
if(last != null)
|
||||
Log.d(TAG, "last = "+ last.toString());
|
||||
}
|
||||
|
||||
|
||||
if(last != null)
|
||||
mPath.add(new PathOverlay(last, p, Color.RED));
|
||||
|
||||
last = p;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a JSON location object into a GeoPoint
|
||||
* @param point
|
||||
* @return Geopoint parsed from the provided JSON Object
|
||||
* @throws JSONException
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private GeoPoint getGeoPoint(JSONObject point) throws JSONException{
|
||||
return new GeoPoint((int) (point.getDouble("lat")*1E6), (int) (point.getDouble("lng")*1E6));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param step
|
||||
* @return the duration of a step
|
||||
* @throws JSONException
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private String getDuration(JSONObject step) throws JSONException{
|
||||
return step.getJSONObject("duration").getString("text");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param step
|
||||
* @return the distance of a step
|
||||
* @throws JSONException
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private String getDistance(JSONObject step) throws JSONException{
|
||||
return step.getJSONObject("distance").getString("text");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the array of PathOverlays
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public ArrayList<PathOverlay> getPath(){
|
||||
return mPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the directions overlay from the map view
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public void removePath() {
|
||||
if(mMapView.getOverlays().removeAll(mPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public interface OnDirectionsCompleteListener{
|
||||
public void onDirectionsComplete(DirectionsOverlay directionsOverlay);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public ArrayList<String> getDirections() {
|
||||
return mDirections;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public ArrayList<GeoPoint> getPoints() {
|
||||
return mPoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public ArrayList<String> getDurations(){
|
||||
return mDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public ArrayList<String> getDistances(){
|
||||
return mDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public String getCopyrights(){
|
||||
return mCopyRights;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public ArrayList<String> getWarnings() {
|
||||
return mWarnings;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* @author Twenty Codes
|
||||
* @author - WWPowers 3-26-2010
|
||||
* @author ricky barrette 9-29-2010
|
||||
*/
|
||||
|
||||
package com.TwentyCodes.android.FindMyCarLib.UI;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Point;
|
||||
|
||||
import com.TwentyCodes.android.FindMyCarLib.R;
|
||||
import com.google.android.maps.GeoPoint;
|
||||
import com.google.android.maps.MapView;
|
||||
import com.google.android.maps.Overlay;
|
||||
import com.google.android.maps.Projection;
|
||||
|
||||
/**
|
||||
* this class will be used as an overlay that will be added to a mapview's <overlay> list.
|
||||
* @author ricky barrette
|
||||
* @date Sep 29, 2010
|
||||
*/
|
||||
public class FindMyCarOverlay extends Overlay {
|
||||
|
||||
public static final String TAG = "FindMyCarOverlay";
|
||||
private Context mContext;
|
||||
private GeoPoint mPoint;
|
||||
|
||||
/**
|
||||
* a simple car overlay item that will be added to the map view's overlay list
|
||||
* @param context
|
||||
* @param geopoint of where the overlay is representing
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public FindMyCarOverlay(Context context, GeoPoint geopoint) {
|
||||
mContext = context;
|
||||
mPoint = geopoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* we override this methods so we can provide a drawable and a location to draw on the canvas.
|
||||
* (non-Javadoc)
|
||||
* @see com.google.android.maps.Overlay#draw(android.graphics.Canvas, com.google.android.maps.MapView, boolean)
|
||||
* @param canvas
|
||||
* @param mapView
|
||||
* @param shadow
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public void draw(Canvas canvas, MapView mapView, boolean shadow){
|
||||
Bitmap carBitmap = BitmapFactory.decodeResource( mContext.getResources(), R.drawable.car);
|
||||
Projection projection = mapView.getProjection();
|
||||
Point point = null;
|
||||
point = projection.toPixels(mPoint, point);
|
||||
canvas.drawBitmap(
|
||||
carBitmap,
|
||||
point.x - (carBitmap.getWidth() / 2),
|
||||
point.y - (carBitmap.getHeight() / 2),
|
||||
null
|
||||
);
|
||||
super.draw(canvas, mapView, shadow);
|
||||
}
|
||||
|
||||
/**
|
||||
* displays the context menu when user taps on car overlay
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public boolean onTap(GeoPoint point, MapView mapView){
|
||||
Point pointTap = mapView.getProjection().toPixels(point, null);
|
||||
Point pointMap = mapView.getProjection().toPixels(mPoint, null);
|
||||
if (pointTap.x - pointMap.x >= -25
|
||||
&& pointTap.x - pointMap.x <= 25
|
||||
&& pointMap.y - pointTap.y >= -25
|
||||
&& pointMap.y - pointTap.y <= 25) {
|
||||
//TODO overlay was taped
|
||||
return true;
|
||||
}
|
||||
super.onTap(point, mapView);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* PathOverlay.java
|
||||
* @date Nov 11, 2011
|
||||
* @author ricky barrette
|
||||
* @author Twenty Codes, LLC
|
||||
*/
|
||||
package com.TwentyCodes.android.FindMyCarLib.UI;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.RectF;
|
||||
|
||||
import com.google.android.maps.GeoPoint;
|
||||
import com.google.android.maps.MapView;
|
||||
import com.google.android.maps.Overlay;
|
||||
import com.google.android.maps.Projection;
|
||||
|
||||
/**
|
||||
* This overlay class is used to draw a path and points on a map
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public class PathOverlay extends Overlay {
|
||||
|
||||
private static final int PATH = 0;
|
||||
private static final int POINT = 1;
|
||||
private GeoPoint mStart;
|
||||
private GeoPoint mEnd;
|
||||
private int mColor;
|
||||
private int mMode;
|
||||
private int mRadius;
|
||||
|
||||
/**
|
||||
* Creates a new PathOverlay in path mode
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public PathOverlay(GeoPoint start, GeoPoint end, int color) {
|
||||
mStart = start;
|
||||
mEnd = end;
|
||||
mColor = color;
|
||||
mMode = PATH;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new PathOverlay in point mode. This is used to draw end points.
|
||||
* @param point
|
||||
* @param radius
|
||||
* @param color
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public PathOverlay(GeoPoint point, int radius, int color){
|
||||
mMode = POINT;
|
||||
mRadius = radius;
|
||||
mStart = point;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param canvas canvas to be drawn on
|
||||
* @param mapView
|
||||
* @param shadow
|
||||
* @param when
|
||||
*/
|
||||
@Override
|
||||
public void draw(Canvas canvas, MapView mapView, boolean shadow) {
|
||||
Projection projection = mapView.getProjection();
|
||||
Paint paint = new Paint();
|
||||
paint.setColor(mColor);
|
||||
paint.setAntiAlias(true);
|
||||
Point point = new Point();
|
||||
projection.toPixels(mStart, point);
|
||||
|
||||
switch (mMode){
|
||||
case POINT:
|
||||
RectF oval = new RectF(point.x - mRadius, point.y - mRadius, point.x + mRadius, point.y + mRadius);
|
||||
canvas.drawOval(oval, paint);
|
||||
case PATH:
|
||||
Point point2 = new Point();
|
||||
projection.toPixels(mEnd, point2);
|
||||
paint.setStrokeWidth(5);
|
||||
paint.setAlpha(120);
|
||||
canvas.drawLine(point.x, point.y, point2.x, point2.y, paint);
|
||||
}
|
||||
super.draw(canvas, mapView, shadow);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @author Twenty Codes
|
||||
* @author ricky barrette
|
||||
*/
|
||||
package com.TwentyCodes.android.FindMyCarLib.UI;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import android.preference.Preference;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
/**
|
||||
* this class will be a simple TextView to be used in a preference activity. you set the text using the set title tag
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public class TextViewPreference extends Preference {
|
||||
|
||||
/**
|
||||
* creates a preference that is nothing but a text view
|
||||
* @param context
|
||||
*/
|
||||
public TextViewPreference(Context context) {
|
||||
super(context);
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a preference that is nothing but a text view
|
||||
* @param context
|
||||
* @param attrs
|
||||
*/
|
||||
public TextViewPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a preference that is nothing but a text view
|
||||
* @param context
|
||||
* @param attrs
|
||||
* @param defStyle
|
||||
*/
|
||||
public TextViewPreference(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* creates a linear layout the contains only a textview.
|
||||
* (non-Javadoc)
|
||||
* @see android.preference.Preference#onCreateView(android.view.ViewGroup)
|
||||
* @param parent
|
||||
* @return
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
protected View onCreateView(ViewGroup parent){
|
||||
|
||||
/*
|
||||
* create a vertical linear layout that width and height that wraps content
|
||||
*/
|
||||
LinearLayout layout = new LinearLayout(getContext());
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
params.gravity = Gravity.CENTER;
|
||||
layout.setPadding(15, 5, 10, 5);
|
||||
layout.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
layout.removeAllViews();
|
||||
|
||||
/*
|
||||
* create a textview that will be used to display the title provided in xml
|
||||
* and add it to the lay out
|
||||
*/
|
||||
TextView title = new TextView(getContext());
|
||||
title.setText(getTitle());
|
||||
title.setTextSize(16);
|
||||
title.setTypeface(Typeface.SANS_SERIF);
|
||||
title.setGravity(Gravity.LEFT);
|
||||
title.setLayoutParams(params);
|
||||
|
||||
/*
|
||||
* add the title and the time picker views to the layout
|
||||
*/
|
||||
layout.addView(title);
|
||||
layout.setId(android.R.id.widget_frame);
|
||||
|
||||
return layout;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* DirectionsListFragment.java
|
||||
* @date Nov 25, 2011
|
||||
* @author ricky barrette
|
||||
* @author Twenty Codes, LLC
|
||||
*/
|
||||
package com.TwentyCodes.android.FindMyCarLib.UI.fragments;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.support.v4.app.ListFragment;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
|
||||
import com.TwentyCodes.android.FindMyCarLib.DirectionsAdapter;
|
||||
import com.TwentyCodes.android.FindMyCarLib.UI.DirectionsOverlay;
|
||||
import com.google.android.maps.GeoPoint;
|
||||
|
||||
/**
|
||||
* This fragment will be used to display directions to the user.
|
||||
* When a specific direction is clicked, the corrispoding geopoint is returned via listener
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public class DirectionsListFragment extends ListFragment {
|
||||
|
||||
public interface OnDirectionSelectedListener{
|
||||
public void onDirectionSelected(GeoPoint SelectedPoint);
|
||||
}
|
||||
|
||||
private OnDirectionSelectedListener mListener;
|
||||
private ArrayList<GeoPoint> mPoints;
|
||||
|
||||
/**
|
||||
* Creates a new Directions List Fragment
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public DirectionsListFragment() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new Directions List Fragment
|
||||
* @param listener
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public DirectionsListFragment(OnDirectionSelectedListener listener) {
|
||||
this();
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the directions from the provided DirectionsOverlay object
|
||||
* @param directions
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public void setDirections(final DirectionsOverlay directions) {
|
||||
mPoints = directions.getPoints();
|
||||
this.setListAdapter(new DirectionsAdapter(getActivity(), directions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a list item is clicked.
|
||||
* Checks to see if the list item is a direction, if to it reports the selected direction's geopoint to the listener
|
||||
* (non-Javadoc)
|
||||
* @see android.widget.AdapterView.OnItemClickListener#onItemClick(android.widget.AdapterView, android.view.View, int, long)
|
||||
*/
|
||||
@Override
|
||||
public void onListItemClick(ListView l, View w, int position, long id) {
|
||||
if(position < mPoints.size())
|
||||
if(mListener != null)
|
||||
mListener.onDirectionSelected(mPoints.get(position));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all content in the listview
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public void clear() {
|
||||
this.setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, new ArrayList<String>()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,714 @@
|
||||
/**
|
||||
* @author Twenty Codes
|
||||
* @author WWPowers
|
||||
* @author ricky barrette
|
||||
* @date 3-26-2010
|
||||
*/
|
||||
package com.TwentyCodes.android.FindMyCarLib.UI.fragments;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Vibrator;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.TwentyCodes.android.FindMyCarLib.Main;
|
||||
import com.TwentyCodes.android.FindMyCarLib.ParkignTimerActivity;
|
||||
import com.TwentyCodes.android.FindMyCarLib.R;
|
||||
import com.TwentyCodes.android.FindMyCarLib.Settings;
|
||||
import com.TwentyCodes.android.FindMyCarLib.UI.DirectionsOverlay;
|
||||
import com.TwentyCodes.android.FindMyCarLib.UI.DirectionsOverlay.OnDirectionsCompleteListener;
|
||||
import com.TwentyCodes.android.FindMyCarLib.UI.FindMyCarOverlay;
|
||||
import com.TwentyCodes.android.SkyHook.SkyHookUserOverlay;
|
||||
import com.TwentyCodes.android.location.GeoPointLocationListener;
|
||||
import com.TwentyCodes.android.location.GeoUtils;
|
||||
import com.TwentyCodes.android.location.MapView;
|
||||
import com.TwentyCodes.android.location.MidPoint;
|
||||
import com.google.android.maps.GeoPoint;
|
||||
|
||||
/**
|
||||
* this is the main class FindMyCar Full
|
||||
* @author WWPowers
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public class MapFragment extends Fragment implements GeoPointLocationListener, OnDirectionsCompleteListener, OnClickListener {
|
||||
|
||||
public boolean hasLeftCar;
|
||||
public boolean isCarFound;
|
||||
// private boolean isShowingBoth = false;
|
||||
private static final int ACCURACY = 0;
|
||||
private static final int DISTANCE = 1;
|
||||
private static final int FOUND_CAR = 2;
|
||||
private static final int SHOWBOTH = 3;
|
||||
protected static final int MIDPOINT = 4;
|
||||
private static final String TAG = "FindMyCarFull";
|
||||
public static FindMyCarOverlay mCarOverlay;
|
||||
public static GeoPoint mCarPoint;
|
||||
public static TextView mDistance;
|
||||
public static boolean isMetric = true;
|
||||
public static TextView mAccuracy;
|
||||
public static SkyHookUserOverlay mUserOverlay;
|
||||
public static MapView mMapView;
|
||||
private SharedPreferences mSettings;
|
||||
private Handler mHandler;
|
||||
private ProgressDialog mProgress;
|
||||
private boolean isGPSDialogEnabled = true;
|
||||
protected DirectionsOverlay mDirections;
|
||||
private MapFragmentListener mListener;
|
||||
|
||||
public interface MapFragmentListener{
|
||||
public void onCarDeleted();
|
||||
public void onDirectionsDisplayed(DirectionsOverlay directions);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a string distance that is based on the users measurement unit preference
|
||||
* @param distance in kilometers
|
||||
* @return string distance
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public static String distance(double distance) {
|
||||
DecimalFormat threeDForm = new DecimalFormat("#.###");
|
||||
DecimalFormat twoDForm = new DecimalFormat("#.##");
|
||||
|
||||
/*
|
||||
* if blnUnit is true, the distance computed will be in metric units,
|
||||
* else, standard units are used
|
||||
* meters are used until 1 kilometer is reached, then kilometers are used
|
||||
* feet are used until 1 mile is reached, then miles are used
|
||||
*/
|
||||
if(isMetric){
|
||||
if (distance < 1){
|
||||
distance = distance * 1000;
|
||||
return twoDForm.format(distance) +" m";
|
||||
}
|
||||
return threeDForm.format(distance) +" Km";
|
||||
}
|
||||
distance = distance / 1.609344;
|
||||
if (distance < 1){
|
||||
distance = distance * 5280;
|
||||
return twoDForm.format(distance) +" ft";
|
||||
}
|
||||
return twoDForm.format(distance) +" mi";
|
||||
}
|
||||
|
||||
/**
|
||||
* pans maps to where the a geopoint is, and if zoomIn is true, zooms in to level 20
|
||||
* @param GeoPoint point - lat and lon of point to pan to
|
||||
* @param boolean zoomIn - true if map needs to be zoomed in
|
||||
* @return boolean false it geopoint is null
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public boolean panToGeoPoint(GeoPoint point, boolean zoomIn) {
|
||||
if (point != null) {
|
||||
if (mMapView != null) {
|
||||
try {
|
||||
mMapView.getController().stopAnimation(false);
|
||||
mMapView.getController().setCenter(point);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (zoomIn) {
|
||||
mMapView.getController().setZoom((mMapView.getMaxZoomLevel() - 2));
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "panToGeoPoint call. mapcontroller was null");
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "panToGeoPoint call. geopoint was null");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* removes the car overlay from the mapview.
|
||||
* @return true if successful
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public boolean removeCar(){
|
||||
|
||||
mCarPoint = null;
|
||||
mDistance.setText("0");
|
||||
mSettings.edit().remove(Settings.LAT).remove(Settings.LON).commit();
|
||||
if(mListener != null)
|
||||
mListener.onCarDeleted();
|
||||
if(mDirections != null) {
|
||||
mDirections.removePath();
|
||||
mDirections = null;
|
||||
}
|
||||
|
||||
try {
|
||||
mMapView.getOverlays().remove(mCarOverlay);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public MapView getMap(){
|
||||
return mMapView;
|
||||
}
|
||||
|
||||
/**
|
||||
* loads saved settings from files
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private void loadSettings(){
|
||||
|
||||
int lat = mSettings.getInt(Settings.LAT, 0);//mFileStream.readInteger(getString(R.string.lat));
|
||||
int lon = mSettings.getInt(Settings.LON, 0);//mFileStream.readInteger(getString(R.string.lon));
|
||||
|
||||
//sets car geopoint up if lat and lon != 0
|
||||
if (lat != 0 && lon != 0) {
|
||||
setCar(new GeoPoint(lat, lon));
|
||||
}
|
||||
|
||||
//sets measurement unit preference
|
||||
String mu = mSettings.getString(Settings.MEASUREMENT_UNIT, null);
|
||||
if(mu != null){
|
||||
if(mu.equalsIgnoreCase("Standard")){
|
||||
isMetric = false;
|
||||
}
|
||||
if(mu.equalsIgnoreCase("Metric")){
|
||||
isMetric = true;
|
||||
}
|
||||
}
|
||||
|
||||
//load compass options
|
||||
String compass_option = mSettings.getString(Settings.COMPASS_OPTION, "Small");
|
||||
int px;
|
||||
Resources r = getResources();
|
||||
if(compass_option.equalsIgnoreCase("Large")){
|
||||
px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 110, r.getDisplayMetrics());
|
||||
mUserOverlay.setCompassDrawables(R.drawable.needle_lrg, R.drawable.compass_lrg, px, px);
|
||||
} else if(compass_option.equalsIgnoreCase("Small")){
|
||||
px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, r.getDisplayMetrics());
|
||||
mUserOverlay.setCompassDrawables(R.drawable.needle_sm, R.drawable.compass_sm, px, px);
|
||||
} else {
|
||||
px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 70, r.getDisplayMetrics());
|
||||
mUserOverlay.setCompassDrawables(R.drawable.needle_med, R.drawable.compass_med, px, px);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* using the users lat/lon saves car location to lat/lon files and passes that geopoint info to setCar
|
||||
* also writes address to notes
|
||||
* @author ricky barrette 3-31-2010
|
||||
* @author WWPowers 3-31-2010
|
||||
*/
|
||||
private void markCar() {
|
||||
//removed old parking timer
|
||||
// ParkingTimerDialog.stopTimer(this);
|
||||
|
||||
GeoPoint user = mUserOverlay.getUserLocation();
|
||||
|
||||
/*
|
||||
* if the user location is not null then
|
||||
* save car lat and lon to files
|
||||
* pass geopoint info to set car, which will setup and show the car overlay
|
||||
* get address info and add it to the notes file
|
||||
*
|
||||
* else inform user that they dont have a gps signal
|
||||
*/
|
||||
if (user != null){
|
||||
mSettings.edit()
|
||||
.putInt(Settings.LAT, user.getLatitudeE6())
|
||||
.putInt(Settings.LON, user.getLongitudeE6())
|
||||
.commit();
|
||||
|
||||
setCar(user);
|
||||
|
||||
//TODO get address
|
||||
|
||||
} else {
|
||||
Toast.makeText(getActivity(), R.string.no_gps_signal, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* ask user if they want to replace current car marker with a new one
|
||||
* @since 0.1.1
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public void markCarDialog(){
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setMessage(R.string.mark_car_warning).setCancelable(false)
|
||||
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
removeCar();
|
||||
markCar();
|
||||
dialog.cancel();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dialog.cancel();
|
||||
}
|
||||
})
|
||||
.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-Javadoc)
|
||||
* @see android.support.v4.app.Fragment#onCreateOptionsMenu(android.view.Menu, android.view.MenuInflater)
|
||||
*/
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.map_action_bar, menu);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.google.android.maps.MapActivity#onCreate(android.os.Bundle)
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
setHasOptionsMenu(true);
|
||||
container.removeAllViews();
|
||||
|
||||
View view = inflater.inflate(R.layout.map, container, false);
|
||||
|
||||
mMapView = (MapView) view.findViewById(R.id.mapview);
|
||||
setUiHandler();
|
||||
|
||||
mAccuracy = (TextView) view.findViewById(R.id.tvAccuracy2);
|
||||
mDistance = (TextView) view.findViewById(R.id.tvDistance2);
|
||||
mSettings = getActivity().getSharedPreferences(Settings.SETTINGS, Context.MODE_WORLD_WRITEABLE);
|
||||
|
||||
view.findViewById(R.id.my_location).setOnClickListener(this);
|
||||
view.findViewById(R.id.mark_my_location).setOnClickListener(this);
|
||||
view.findViewById(R.id.show_both).setOnClickListener(this);
|
||||
view.findViewById(R.id.parking_timer).setOnClickListener(this);
|
||||
view.findViewById(R.id.directions).setOnClickListener(this);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDirectionsComplete(final DirectionsOverlay directionsOverlay) {
|
||||
if(mProgress != null){
|
||||
mProgress.dismiss();
|
||||
mProgress = null;
|
||||
}
|
||||
if(mListener != null)
|
||||
mListener.onDirectionsDisplayed(directionsOverlay);
|
||||
}
|
||||
/**
|
||||
* here we will overrride onLocationChanged() so we can update the FindMyCarUI
|
||||
* (non-Javadoc)
|
||||
* @see com.TwentyCodes.android.SkyHook.map.SkyHookUserOverlay#onLocationChanged(com.google.android.maps.GeoPoint, int)
|
||||
* @param point
|
||||
* @param accuracy
|
||||
* @author Ricky Barrette
|
||||
*/
|
||||
@Override
|
||||
public void onLocationChanged(final GeoPoint point, final int accuracy){
|
||||
|
||||
Log.d(TAG, "FMC onLocationChanged()");
|
||||
|
||||
new Thread(new Runnable(){
|
||||
@Override
|
||||
public void run(){
|
||||
mHandler.sendMessage(mHandler.obtainMessage(ACCURACY, distance(accuracy/1E3)));
|
||||
|
||||
// if (point != null)
|
||||
// if(isShowingBoth)
|
||||
// if(GeoUtils.isPointOffMap(mMapView,point) || GeoUtils.isPointOffMap(mMapView, mCarPoint))
|
||||
// mHandler.sendEmptyMessage(SHOWBOTH);
|
||||
//
|
||||
if (mCarPoint != null && point != null){
|
||||
double distance = GeoUtils.distanceKm(point, mCarPoint);
|
||||
mHandler.sendMessage(mHandler.obtainMessage(DISTANCE, distance(distance)));
|
||||
|
||||
//value is set in KM. if user has gone 30 feet from car app is set to check for arrival
|
||||
if (distance > 0.009144){
|
||||
hasLeftCar = true;
|
||||
}
|
||||
//if user has gone back into 30 foot radius and has not found the car and has left the car then notify user of finding of car
|
||||
if (distance <= 0.009144 && isCarFound == false && hasLeftCar == true){
|
||||
isCarFound = true;
|
||||
|
||||
mHandler.sendEmptyMessage(FOUND_CAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* handles menu selection
|
||||
* @since 0.0.2
|
||||
* @author ricky barrette 3-30-2010
|
||||
*/
|
||||
@Override
|
||||
public boolean onOptionsItemSelected (MenuItem item) {
|
||||
if(item.getItemId() == R.id.delete_car)
|
||||
removeCar();
|
||||
else if (item.getItemId() == R.id.settings) {
|
||||
startActivity(new Intent().setClass(getActivity(), Settings.class));
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.map_mode)
|
||||
changeMapMode();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (non-Javadoc)
|
||||
* @see android.support.v4.app.Fragment#onPause()
|
||||
*/
|
||||
@Override
|
||||
public void onPause() {
|
||||
mUserOverlay.disableCompass();
|
||||
mUserOverlay.disableMyLocation();
|
||||
mMapView.getOverlays().remove(mUserOverlay);
|
||||
mUserOverlay = null;
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called each time the menu is created starting with the second instance.
|
||||
* Used to dynamicly modify the options menu
|
||||
*/
|
||||
|
||||
/**
|
||||
* (non-Javadoc)
|
||||
* @see android.support.v4.app.Fragment#onResume()
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
if(mUserOverlay == null)
|
||||
mUserOverlay = new SkyHookUserOverlay(mMapView, getActivity());
|
||||
|
||||
mMapView.getOverlays().add(mUserOverlay);
|
||||
|
||||
//start all location based services
|
||||
mUserOverlay.enableMyLocation();
|
||||
if(!isGPSDialogEnabled)
|
||||
mUserOverlay.disableGPSDialog();
|
||||
|
||||
mUserOverlay.registerListener(this);
|
||||
mUserOverlay.enableCompass();
|
||||
|
||||
loadSettings();
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
/**
|
||||
* reorders the overlays to the UserOverlay always on top
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private void reorderOverlays() {
|
||||
mMapView.getOverlays().remove(mUserOverlay);
|
||||
mMapView.getOverlays().add(mUserOverlay);
|
||||
}
|
||||
|
||||
/**
|
||||
* removes the previous car overlay and replaces it with a new car overlay that
|
||||
* represents the users car at a specific geopoint
|
||||
*
|
||||
* @param point for geopoint of car
|
||||
* @author WWPowers 3-31-2010
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public void setCar(GeoPoint point) {
|
||||
isCarFound = false;
|
||||
hasLeftCar = false;
|
||||
mCarPoint = point;
|
||||
mCarOverlay = new FindMyCarOverlay(getActivity(), point);
|
||||
mMapView.getOverlays().add(mCarOverlay);
|
||||
mUserOverlay.setDestination(mCarPoint);
|
||||
reorderOverlays();
|
||||
}
|
||||
|
||||
/**
|
||||
* enables the GPS dialog
|
||||
* @param b
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public void setGPSDialogEnabled(boolean b) {
|
||||
isGPSDialogEnabled = b;
|
||||
if(mUserOverlay != null)
|
||||
if(b)
|
||||
mUserOverlay.enableGPSDialog();
|
||||
else
|
||||
mUserOverlay.disableGPSDialog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the UI handler.
|
||||
* The UI handler will process messages from processing threads
|
||||
*
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private void setUiHandler() {
|
||||
mHandler = new Handler(){
|
||||
@Override
|
||||
public void handleMessage(Message msg){
|
||||
switch(msg.what){
|
||||
case ACCURACY:
|
||||
mAccuracy.setText( (String) msg.obj );
|
||||
break;
|
||||
case DISTANCE:
|
||||
mDistance.setText((String) msg.obj);
|
||||
break;
|
||||
case FOUND_CAR:
|
||||
/* remove the directions overlay & delete all navigation files when the car is found
|
||||
* this will prevent old directions from being displayed after the car is found.
|
||||
*/
|
||||
|
||||
if(mDirections != null) {
|
||||
mDirections.removePath();
|
||||
mDirections = null;
|
||||
}
|
||||
|
||||
Vibrator vib = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.yay)
|
||||
.setMessage(R.string.found_car).setCancelable(false)
|
||||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
|
||||
}
|
||||
}).show();
|
||||
vib.vibrate(100);
|
||||
mDistance.setText("0");
|
||||
break;
|
||||
case SHOWBOTH:
|
||||
showBoth();
|
||||
break;
|
||||
case MIDPOINT:
|
||||
MidPoint mp = (MidPoint) msg.obj;
|
||||
panToGeoPoint(mp.getMidPoint(), false);
|
||||
mp.zoomToSpan(mMapView);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* computes a geopoint the is the central geopoint between the user and the car.
|
||||
* also it zooms so both marks are visible on the map
|
||||
* @author ricky barrette
|
||||
*/
|
||||
protected void showBoth(){
|
||||
if(mUserOverlay != null) {
|
||||
if (mCarPoint == null){
|
||||
Toast.makeText(getActivity(), R.string.mark_car_first, Toast.LENGTH_LONG).show();
|
||||
} else if (mUserOverlay.getUserLocation() == null){
|
||||
Toast.makeText(getActivity(), R.string.no_gps_signal, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
if (mMapView != null) {
|
||||
mMapView.getController().stopAnimation(false);
|
||||
mUserOverlay.followUser(false);
|
||||
// isShowingBoth = true;
|
||||
final GeoPoint user = mUserOverlay.getUserLocation();
|
||||
|
||||
/*
|
||||
* here we null check our next set of value before we send them off to geoutils
|
||||
* if they have became null for some reason we disable show both mode
|
||||
*/
|
||||
if(mCarPoint!= null && user != null){
|
||||
new Thread(new Runnable(){
|
||||
@Override
|
||||
public void run(){
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MIDPOINT, GeoUtils.midPoint(mCarPoint, user)));
|
||||
}
|
||||
}).start();
|
||||
|
||||
}
|
||||
// else
|
||||
// isShowingBoth = false;
|
||||
|
||||
|
||||
} else {
|
||||
Log.e(TAG, "showBoth.mMapView is null");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the listener for this map fragment
|
||||
* @param listener
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public void setMapFragmentListener(MapFragmentListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v.getId() == R.id.show_both)
|
||||
showBoth();
|
||||
else if(v.getId() == R.id.mark_my_location)
|
||||
markMyLocation();
|
||||
else if (v.getId() == R.id.my_location)
|
||||
myLocation();
|
||||
else if (v.getId() == R.id.directions)
|
||||
directions();
|
||||
else if (v.getId() == R.id.map_mode)
|
||||
changeMapMode();
|
||||
else if (v.getId() == R.id.parking_timer)
|
||||
if (! Main.isFull)
|
||||
Main.featureInFullDialog(getActivity());
|
||||
else
|
||||
getActivity().startActivity(new Intent(getActivity(), ParkignTimerActivity.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the user's location
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private void markMyLocation() {
|
||||
mUserOverlay.followUser(true);
|
||||
// isShowingBoth = false;
|
||||
|
||||
/*
|
||||
* if we have a gps signal, then pan to user location and then
|
||||
* if there is no car, mark the car location as the users location
|
||||
* else show mark car dialog
|
||||
*
|
||||
* we switch from MyLocationOverlay.getMyLocation() to referencing the static variable MyCustomLocationOverlay.gpUser
|
||||
* because for some reason getMyLocation() would become null.
|
||||
* @author ricky barrette
|
||||
*/
|
||||
if (! panToGeoPoint(mUserOverlay.getUserLocation(), true)){
|
||||
Toast.makeText(getActivity(), R.string.no_gps_signal, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
if (mCarPoint != null){
|
||||
markCarDialog();
|
||||
} else {
|
||||
markCar();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* pans the map to the user's location
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private void myLocation() {
|
||||
mUserOverlay.followUser(true);
|
||||
// isShowingBoth = false;
|
||||
|
||||
/*
|
||||
* if we have a gps signal, then pan to user location
|
||||
* else notify user that there is no GPS signal
|
||||
*
|
||||
* we switch from MyLocationOverlay.getMyLocation() to referencing the static variable MyCustomLocationOverlay.gpUser
|
||||
* because for some reason getMyLocation() would become null.
|
||||
* @author ricky barrette
|
||||
*/
|
||||
if (! panToGeoPoint(mUserOverlay.getUserLocation(), true)){
|
||||
Toast.makeText(getActivity(), R.string.no_gps_signal, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the walking directions on the map
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private void directions() {
|
||||
if (Main.isFull) {
|
||||
/*
|
||||
* if there is no car marked, then notify user
|
||||
* else check to see if there is directions
|
||||
*/
|
||||
if (mCarPoint == null) {
|
||||
Toast.makeText(getActivity(), R.string.mark_car_first, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
|
||||
/*
|
||||
* Remove old directions if the exist
|
||||
*/
|
||||
if(mDirections != null)
|
||||
mDirections.removePath();
|
||||
|
||||
/*
|
||||
* if there is no location fix then notify user
|
||||
* else download directions and display them
|
||||
*/
|
||||
if (mUserOverlay.getUserLocation() == null) {
|
||||
Toast.makeText(getActivity(), R.string.no_gps_signal, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
|
||||
mProgress = ProgressDialog.show(getActivity(), getText(R.string.directions), getText(R.string.calculating), true);
|
||||
new Thread(new Runnable(){
|
||||
|
||||
/**
|
||||
* Notifys user about the error that occurred outside of the UI thread
|
||||
* @param e
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public void notify(final Exception e){
|
||||
e.printStackTrace();
|
||||
getActivity().runOnUiThread(new Runnable(){
|
||||
@Override
|
||||
public void run(){
|
||||
Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
|
||||
mProgress.dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(){
|
||||
try {
|
||||
mDirections = new DirectionsOverlay(mMapView, mUserOverlay.getUserLocation(), mCarPoint, MapFragment.this);
|
||||
} catch (IllegalStateException e) {
|
||||
notify(e);
|
||||
} catch (ClientProtocolException e) {
|
||||
notify(e);
|
||||
} catch (IOException e) {
|
||||
notify(e);
|
||||
} catch (JSONException e) {
|
||||
notify(e);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} else
|
||||
Main.featureInFullDialog(getActivity());
|
||||
}
|
||||
|
||||
/**
|
||||
* changes the map mode
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private void changeMapMode() {
|
||||
if(mMapView != null)
|
||||
mMapView.setSatellite(!mMapView.isSatellite());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
* @author Twenty Codes
|
||||
* @author ricky barrette
|
||||
*/
|
||||
|
||||
package com.TwentyCodes.android.FindMyCarLib.UI.fragments;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.TwentyCodes.android.FindMyCarLib.R;
|
||||
import com.TwentyCodes.android.FindMyCarLib.Settings;
|
||||
|
||||
/**
|
||||
* displays a notes dialog where the user can enter and save notes
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public class NotesFragment extends Fragment {
|
||||
|
||||
private EditText mText;
|
||||
private SharedPreferences mSettings;
|
||||
private TextView mAddress;
|
||||
|
||||
/**
|
||||
* (non-Javadoc)
|
||||
* @see android.support.v4.app.Fragment#onPause()
|
||||
*/
|
||||
@Override
|
||||
public void onPause() {
|
||||
mSettings.edit().putString(Settings.NOTE, mText.getText().toString()).commit();
|
||||
// Toast.makeText(getActivity(), R.string.saved, Toast.LENGTH_LONG).show();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-Javadoc)
|
||||
* @see android.support.v4.app.Fragment#onResume()
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
/*
|
||||
* display saved notes from file
|
||||
*/
|
||||
mText.setText(mSettings.getString(Settings.NOTE, ""));
|
||||
|
||||
/*
|
||||
* display address from file
|
||||
*/
|
||||
mAddress.setText(mSettings.getString(Settings.ADDRESS, ""));
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater,
|
||||
* android.view.ViewGroup, android.os.Bundle)
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.notes, container, false);
|
||||
mSettings = getActivity().getSharedPreferences(Settings.SETTINGS, 0);
|
||||
mText = (EditText) view.findViewById(R.id.editText);
|
||||
mAddress = (TextView) view.findViewById(R.id.tvAddress);
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes the note
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public void delete() {
|
||||
if(mText != null)
|
||||
mText.setText("");
|
||||
if(mAddress != null)
|
||||
mAddress.setText("");
|
||||
if(mSettings != null)
|
||||
mSettings.edit().remove(Settings.NOTE).remove(Settings.ADDRESS).commit();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
/**
|
||||
* @author Twenty Codes
|
||||
* @author WWPowers
|
||||
* @author ricky barrette
|
||||
*/
|
||||
package com.TwentyCodes.android.FindMyCarLib.UI.fragments;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.text.Editable;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TimePicker;
|
||||
import android.widget.TimePicker.OnTimeChangedListener;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.TwentyCodes.android.FindMyCarLib.AlarmReceiver;
|
||||
import com.TwentyCodes.android.FindMyCarLib.ParkingTimerService;
|
||||
import com.TwentyCodes.android.FindMyCarLib.R;
|
||||
import com.TwentyCodes.android.FindMyCarLib.Settings;
|
||||
|
||||
/**
|
||||
* This class will be the dialog that allows the user to input their settings for the parking timer, start a new parking timer, or delete an existing
|
||||
* parking timer.
|
||||
* @author warren
|
||||
*/
|
||||
public class ParkingTimerFragment extends Fragment implements OnClickListener, OnTimeChangedListener {
|
||||
|
||||
private TimePicker mPicker;
|
||||
private CheckBox chNotify;
|
||||
private EditText etNotify;
|
||||
private static boolean mIsOutsideDialog = false;
|
||||
private static final String TAG = "ParkingTimerDialog";
|
||||
private static SharedPreferences settings;
|
||||
|
||||
/**
|
||||
* (non-Javadoc)
|
||||
* @see android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)
|
||||
*/
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
this.getActivity().setTitle(R.string.parking_timer);
|
||||
View view = inflater.inflate(R.layout.parkingtimer_layout, container, false);
|
||||
settings = getActivity().getSharedPreferences(Settings.SETTINGS, 0);
|
||||
mPicker = (TimePicker) view.findViewById(R.id.tpParkingTimerTimePicker);
|
||||
view.findViewById(R.id.btSetTimer).setOnClickListener(this);
|
||||
view.findViewById(R.id.btRemoveTimer).setOnClickListener(this);
|
||||
chNotify = (CheckBox) view.findViewById(R.id.chNotify);
|
||||
etNotify = (EditText) view.findViewById(R.id.etNotify);
|
||||
mPicker.setIs24HourView(true);
|
||||
mPicker.setCurrentHour(0);
|
||||
mPicker.setCurrentMinute(5);
|
||||
mPicker.setOnTimeChangedListener(this);
|
||||
setTitle(mPicker);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v.getId() == R.id.btSetTimer) {
|
||||
setTimer();
|
||||
getActivity().finish();
|
||||
} else if (v.getId() == R.id.btRemoveTimer) {
|
||||
stopTimer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @author - WWPowers
|
||||
* @param value - value to be checked
|
||||
* @return - true if value meets reqs. false if it does not
|
||||
* This method checks if the given value is an integer that is greater than or equal to 1.
|
||||
*/
|
||||
private boolean checkValue(String value) {
|
||||
char chrValue;
|
||||
int num;
|
||||
if (value.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
for(int i = 0; i < value.length(); i++) {
|
||||
chrValue = value.charAt(i);
|
||||
try {
|
||||
num = Integer.parseInt(Character.toString(chrValue));
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
num = Integer.parseInt(value);
|
||||
if (num < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the int value displayed by a edit text
|
||||
* returns 0 if the value is not parse-able
|
||||
* @param EditText text
|
||||
* @return int value
|
||||
* @author WWPowers
|
||||
*/
|
||||
private int getValue(EditText text) {
|
||||
Editable value = text.getText();
|
||||
String strValue = value.toString();
|
||||
if (!checkValue(strValue)) {
|
||||
toastLong(getActivity().getText(R.string.pick_time_greaterthan_zero));
|
||||
} else {
|
||||
return Integer.parseInt(strValue);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cancels the parking timer alarms
|
||||
* @author WWPowers
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public void stopTimer() {
|
||||
settings = getActivity().getSharedPreferences(Settings.SETTINGS, 0);
|
||||
mIsOutsideDialog = true;
|
||||
if (! settings.getBoolean(Settings.PARKING_TIMER_ALARM, false)){
|
||||
if (!mIsOutsideDialog) {
|
||||
toastLong(getActivity().getText(R.string.no_timer));
|
||||
mIsOutsideDialog = true;
|
||||
// //cancel ongoing notification
|
||||
NotificationManager notificationManager = (NotificationManager) getActivity().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.cancel(656);
|
||||
}
|
||||
|
||||
} else {
|
||||
settings.edit().remove(Settings.PARKING_TIMER_ALARM).commit();
|
||||
|
||||
/*
|
||||
* to cancel the alarms we will create a new Intent and a new PendingIntent with the
|
||||
* same requestCode as the PendingIntent alarm we want to cancel.
|
||||
* Note: The intent and PendingIntent have to be the same as the ones used to create the alarms.
|
||||
*/;
|
||||
AlarmManager am = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
|
||||
|
||||
Intent notifyIntent = new Intent(getActivity(), AlarmReceiver.class);
|
||||
notifyIntent.putExtra("requestCode", 0);
|
||||
PendingIntent sender = PendingIntent.getBroadcast(getActivity(), 1234567, notifyIntent, 0);
|
||||
am.cancel(sender);
|
||||
|
||||
Intent timerUpIntent = new Intent(getActivity(), AlarmReceiver.class);
|
||||
timerUpIntent.putExtra("requestCode", 1);
|
||||
PendingIntent sender2 = PendingIntent.getBroadcast(getActivity(), 123123, timerUpIntent, 0);
|
||||
am.cancel(sender2);
|
||||
|
||||
stopTimerService();
|
||||
|
||||
toastLong("Timer Canceled");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* this method is a convince method which will set a timer in the alarm manager class
|
||||
*/
|
||||
private void setTimer() {
|
||||
long hours = mPicker.getCurrentHour();
|
||||
long minutes = mPicker.getCurrentMinute();
|
||||
long notify = getValue(etNotify);
|
||||
if ((hours == 0) && (minutes == 0)) {
|
||||
toastLong(getActivity().getText(R.string.pick_time_greaterthan_zero));
|
||||
} if (((hours * 60) + minutes) > 1440) {
|
||||
toastLong(getActivity().getText(R.string.pick_timer_lessthan_24));
|
||||
} else {
|
||||
|
||||
if (! settings.getBoolean(Settings.PARKING_TIMER_ALARM, false)){
|
||||
|
||||
minutes = minutes + (hours * 60);
|
||||
minutes = minutes * 60000;
|
||||
|
||||
//if the parking timer ongoing notification is enabled, then start the service to display it.
|
||||
if (settings.getBoolean(Settings.PARKING_TIMER_ONGOING_NOTIFICATION_ISENABLED, true)){
|
||||
startTimerService(minutes);
|
||||
}
|
||||
|
||||
notify = getValue(etNotify);
|
||||
|
||||
Calendar cal = Calendar.getInstance();
|
||||
|
||||
Intent notifyIntent = new Intent(getActivity(), AlarmReceiver.class);
|
||||
notifyIntent.putExtra("requestCode", 0);
|
||||
PendingIntent sender = PendingIntent.getBroadcast(getActivity(), 1234567, notifyIntent, 0);
|
||||
|
||||
Intent timerUpIntent = new Intent(getActivity(), AlarmReceiver.class);
|
||||
timerUpIntent.putExtra("requestCode", 1);
|
||||
PendingIntent sender2 = PendingIntent.getBroadcast(getActivity(), 123123, timerUpIntent, 0);
|
||||
|
||||
AlarmManager am = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
|
||||
|
||||
if ((notify != 0) && (chNotify.isChecked()) && (notify < (minutes / 60000))) {
|
||||
notify = notify * 60000;
|
||||
notify = (minutes - notify);
|
||||
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis() + notify, sender);
|
||||
}
|
||||
|
||||
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis() + minutes, sender2);
|
||||
|
||||
toastLong(getActivity().getText(R.string.timer_set_for) +" "+ mPicker.getCurrentHour() +" "+ getActivity().getText(R.string.timer_set_for_hours)
|
||||
+" "+ mPicker.getCurrentMinute() +" "+ getActivity().getText(R.string.timer_set_for_minutes));
|
||||
settings.edit().putBoolean(Settings.PARKING_TIMER_ALARM, true).commit();
|
||||
} else {
|
||||
toastLong(getActivity().getText(R.string.timer_already_set));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* displays a toast message msg
|
||||
* @param msg
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private void toastLong(CharSequence msg) {
|
||||
Toast toast = Toast.makeText(getActivity(), msg, Toast.LENGTH_LONG);
|
||||
toast.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* updates the tile of the parking timer dialog when ever the time picker incrimented
|
||||
* @author wwpowers
|
||||
*/
|
||||
@Override
|
||||
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
|
||||
setTitle(mPicker);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is a convenience method to return the time of the day plus the parking timer
|
||||
* @return - title of dialog as string
|
||||
*/
|
||||
private void setTitle(TimePicker picker) {
|
||||
int hourOfDay = picker.getCurrentHour();
|
||||
int minute = picker.getCurrentMinute();
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
//hours
|
||||
hourOfDay = hourOfDay + calendar.get(Calendar.HOUR_OF_DAY);
|
||||
if (hourOfDay > 24){
|
||||
hourOfDay = hourOfDay - 24;
|
||||
}
|
||||
//minutes
|
||||
minute = minute + calendar.get(Calendar.MINUTE);
|
||||
if (minute > 59) {
|
||||
hourOfDay++;
|
||||
minute = minute - 60;
|
||||
}
|
||||
//update title
|
||||
|
||||
// if (hourOfDay > 12) {
|
||||
// this.setTitle(getActivity().getText(R.string.parking_timer) + " " + padHour(hourOfDay - 12) + ":" + padMinute(minute) + " PM");
|
||||
// } else {
|
||||
// this.setTitle(getActivity().getText(R.string.parking_timer) + " " + (hourOfDay) + ":" + padMinute(minute) + " AM");
|
||||
// }
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Convenience method for fixing 0AM to 12AM
|
||||
// * @param hour
|
||||
// * @return String hour of day
|
||||
// * @author ricky barrette
|
||||
// */
|
||||
// private static String padHour(int hour){
|
||||
// if (hour == 0){
|
||||
// return String.valueOf(12);
|
||||
// }
|
||||
// return String.valueOf(hour);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * Convenience method for formatting time
|
||||
// * @param minute - minute of day
|
||||
// * @return - String of minute of day plus an zero in front of it if the value is less than 10
|
||||
// */
|
||||
// private static String padMinute(int minute) {
|
||||
// if (minute >= 10){
|
||||
// return String.valueOf(minute);
|
||||
// }
|
||||
// return "0" + String.valueOf(minute);
|
||||
// }
|
||||
|
||||
/**
|
||||
* This method will create the remote service, bind to it and pass it the time, then unbind
|
||||
* @param time - length of parking timer in milliseconds
|
||||
*/
|
||||
|
||||
protected void startTimerService(long minutes) {
|
||||
// if (!mServiceStarted) {
|
||||
// Intent i = new Intent();
|
||||
// i.setClassName("com.TwentyCodes.android.FindMyCarFull", "com.TwentyCodes.android.FindMyCarFull.ParkingTimerService");
|
||||
// mCtx.startService(i);
|
||||
// mServiceStarted = true;
|
||||
// Log.i(TAG, "startTimerService.service started");
|
||||
// } else {
|
||||
// Log.i(TAG, "startTimerService.service is already started");
|
||||
// }
|
||||
if (! settings.getBoolean(Settings.PARKING_TIMER_SERVICE, false)) {
|
||||
|
||||
Intent i = new Intent(getActivity(), ParkingTimerService.class);
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putLong("minutes", minutes);
|
||||
if (settings.getString(Settings.PARKING_TIMER_NOTIFICATION_COLOR, "Black").equalsIgnoreCase("Black")) {
|
||||
Log.i(TAG, "startTimerService.retrieved color: " + settings.getString(Settings.PARKING_TIMER_NOTIFICATION_COLOR, "Null"));
|
||||
bundle.putInt("color", R.drawable.show_car_black);
|
||||
} else {
|
||||
bundle.putInt("color", R.drawable.show_car_white);
|
||||
}
|
||||
// bundle.putInt(Settings.PARKING_TIMER_UPDATE_INTERVAL, settings.getInt(Settings.PARKING_TIMER_UPDATE_INTERVAL, 60));
|
||||
i.putExtra("minutes", bundle);
|
||||
getActivity().startService(i);
|
||||
settings.edit().putBoolean(Settings.PARKING_TIMER_SERVICE, true).commit();
|
||||
} else {
|
||||
Log.i(TAG, "startTimerService.service is already started");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will stop the service if it is running
|
||||
*/
|
||||
|
||||
public void stopTimerService() {
|
||||
if (settings.getBoolean(Settings.PARKING_TIMER_SERVICE, false)) {
|
||||
Intent i = new Intent();
|
||||
i.setClassName("com.TwentyCodes.android.FindMyCarFull", "com.TwentyCodes.android.FindMyCarFull.ParkingTimerService");
|
||||
getActivity().stopService(i);
|
||||
NotificationManager notificationManager = (NotificationManager) getActivity().getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.cancel(656);
|
||||
settings.edit().remove(Settings.PARKING_TIMER_SERVICE).commit();
|
||||
} else {
|
||||
Log.i(TAG, "stopTimerService.service is not running. unable to stop");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,526 @@
|
||||
/**
|
||||
* RingerDatabase.java
|
||||
* @date Nov 10, 2011
|
||||
* @author Twenty Codes, LLC
|
||||
* @author ricky barrette
|
||||
*/
|
||||
package com.TwentyCodes.android.FindMyCarLib.db;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.TwentyCodes.android.FindMyCarLib.R;
|
||||
import com.TwentyCodes.android.FindMyCarLib.debug.Debug;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class will be the main interface between find my car and it's database
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public class Database {
|
||||
|
||||
private static final String TAG = "Database";
|
||||
private Context mContext;
|
||||
private SQLiteDatabase mDb;
|
||||
public boolean isUpgrading = false;
|
||||
private DatabaseListener mListener;
|
||||
|
||||
/*
|
||||
* database information values
|
||||
*/
|
||||
private final int DATABASE_VERSION = 1;
|
||||
|
||||
/*
|
||||
* the following is for the table that holds the other table names
|
||||
*/
|
||||
private final String DATABASE_NAME = "locations.db";
|
||||
private final String LOCATION_TABLE = "location";
|
||||
private static final String LOCATION_INFO_TABLE = "location_info";
|
||||
|
||||
/*
|
||||
* Database keys
|
||||
*/
|
||||
public final static String KEY = "key";
|
||||
public final static String KEY_VALUE = "value";
|
||||
public final static String KEY_LOCATION_NAME = "location";
|
||||
public final static String KEY_LAT = "lat";
|
||||
public final static String KEY_LON = "lon";
|
||||
public final static String KEY_IS_ENABLED = "enabled";
|
||||
public final static String KEY_IS_FOUND = "isFound";
|
||||
public final static String KEY_ADDRESS = "address";
|
||||
public final static String KEY_DIRECTIONS = "directions";
|
||||
// public final static String KEY_ = "";
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A helper class to manage database creation and version management.
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private class OpenHelper extends SQLiteOpenHelper {
|
||||
|
||||
/**
|
||||
* Creates a new OpenHelper
|
||||
* @param context
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public OpenHelper(Context context) {
|
||||
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the initial database structure
|
||||
* @param db
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private void createDatabase(SQLiteDatabase db){
|
||||
db.execSQL("CREATE TABLE " + LOCATION_TABLE +
|
||||
"(id INTEGER PRIMARY KEY, " +
|
||||
KEY_LOCATION_NAME+" TEXT, " +
|
||||
KEY_IS_ENABLED+" TEXT)");
|
||||
db.execSQL("CREATE TABLE " + LOCATION_INFO_TABLE +
|
||||
"(id INTEGER PRIMARY KEY, " +
|
||||
KEY_LOCATION_NAME+" TEXT, " +
|
||||
KEY+" TEXT, " +
|
||||
KEY_VALUE+" TEXT)");
|
||||
}
|
||||
|
||||
/**
|
||||
* called when the database is created for the first time. this will create our Ringer database
|
||||
* (non-Javadoc)
|
||||
* @see android.database.sqlite.SQLiteOpenHelper#onCreate(android.database.sqlite.SQLiteDatabase)
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
if(Debug.DROP_TABLE_EVERY_TIME)
|
||||
db.execSQL("DROP TABLE IF EXISTS " + LOCATION_TABLE);
|
||||
createDatabase(db);
|
||||
//insert the default ringer into this table
|
||||
db.execSQL("insert into " + LOCATION_TABLE + "(" + KEY_LOCATION_NAME + ") values ('"+Database.this.mContext.getString(R.string.default_location)+"')");
|
||||
}
|
||||
|
||||
/**
|
||||
* called when the database needs to be updated
|
||||
* (non-Javadoc)
|
||||
* @see android.database.sqlite.SQLiteOpenHelper#onUpgrade(android.database.sqlite.SQLiteDatabase, int, int)
|
||||
* @author ricky barrette
|
||||
*/
|
||||
@Override
|
||||
public void onUpgrade(final SQLiteDatabase db, final int oldVersion, int newVersion) {
|
||||
Log.w(TAG, "Upgrading database from version "+oldVersion+" to "+newVersion);
|
||||
|
||||
if(Database.this.mListener != null)
|
||||
Database.this.mListener.onDatabaseUpgrade();
|
||||
|
||||
Database.this.isUpgrading = true;
|
||||
|
||||
final Handler handler = new Handler(){
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
if(Database.this.mListener != null)
|
||||
Database.this.mListener.onDatabaseUpgradeComplete();
|
||||
}
|
||||
};
|
||||
|
||||
//upgrade thread
|
||||
new Thread( new Runnable(){
|
||||
@Override
|
||||
public void run(){
|
||||
Looper.prepare();
|
||||
switch(oldVersion){
|
||||
case 1:
|
||||
//TODO upgrade to version 2
|
||||
}
|
||||
handler.sendEmptyMessage(0);
|
||||
Database.this.isUpgrading = false;
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string boolean from the database
|
||||
* @param bool
|
||||
* @return true or false
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public static boolean parseBoolean(String bool){
|
||||
try {
|
||||
return bool == null ? false : Integer.parseInt(bool) == 1 ? true : false;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new RingerDatabase
|
||||
* @param context
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public Database(Context context){
|
||||
this.mContext = context;
|
||||
this.mDb = new OpenHelper(this.mContext).getWritableDatabase();
|
||||
}
|
||||
|
||||
public Database(Context context, DatabaseListener listener){
|
||||
this.mListener = listener;
|
||||
this.mContext = context;
|
||||
this.mDb = new OpenHelper(this.mContext).getWritableDatabase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Backs up the database
|
||||
* @return true if successful
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public boolean backup(){
|
||||
File dbFile = new File(Environment.getDataDirectory() + "/data/"+mContext.getPackageName()+"/databases/"+DATABASE_NAME);
|
||||
|
||||
File exportDir = new File(Environment.getExternalStorageDirectory(), "/"+this.mContext.getString(R.string.app_name));
|
||||
if (!exportDir.exists()) {
|
||||
exportDir.mkdirs();
|
||||
}
|
||||
File file = new File(exportDir, dbFile.getName());
|
||||
|
||||
try {
|
||||
file.createNewFile();
|
||||
this.copyFile(dbFile, file);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if this ringer name is original, if not it renames it
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
private String checkName(String name){
|
||||
|
||||
List<String> names = this.getAllLocationNames();
|
||||
String ringerName = name;
|
||||
int count = 1;
|
||||
|
||||
for(int index = 0; index < names.size(); index++ ){
|
||||
if(ringerName.equals(names.get(index))){
|
||||
ringerName = name + count+++"";
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
return ringerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a file
|
||||
* @param src file
|
||||
* @param dst file
|
||||
* @throws IOException
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private void copyFile(File src, File dst) throws IOException {
|
||||
FileChannel inChannel = new FileInputStream(src).getChannel();
|
||||
FileChannel outChannel = new FileOutputStream(dst).getChannel();
|
||||
try {
|
||||
inChannel.transferTo(0, inChannel.size(), outChannel);
|
||||
} finally {
|
||||
if (inChannel != null)
|
||||
inChannel.close();
|
||||
if (outChannel != null)
|
||||
outChannel.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes a note by its row id
|
||||
* @param id
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public void deleteLocation(final long id) {
|
||||
|
||||
final ProgressDialog progress = ProgressDialog.show(Database.this.mContext, "", Database.this.mContext.getText(R.string.deleteing), true, true);
|
||||
|
||||
final Handler handler = new Handler(){
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
if(Database.this.mListener != null)
|
||||
Database.this.mListener.onLocationDeletionComplete();
|
||||
}
|
||||
};
|
||||
|
||||
//ringer deleting thread
|
||||
new Thread( new Runnable(){
|
||||
@Override
|
||||
public void run(){
|
||||
Looper.prepare();
|
||||
|
||||
/*
|
||||
* get the ringer name from the id, and then delete all its information from the ringer information table
|
||||
*/
|
||||
Database.this.mDb.delete(LOCATION_INFO_TABLE, KEY_LOCATION_NAME +" = "+ DatabaseUtils.sqlEscapeString(Database.this.getLocationName(id)), null);
|
||||
|
||||
/*
|
||||
* finally delete the ringer from the ringer table
|
||||
*/
|
||||
Database.this.mDb.delete(LOCATION_TABLE, "id = "+ id, null);
|
||||
updateRowIds(id +1);
|
||||
handler.sendEmptyMessage(0);
|
||||
progress.dismiss();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a cursor containing all ringers
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public Cursor getAllLocations(){
|
||||
return this.mDb.query(LOCATION_TABLE, new String[] { KEY_LOCATION_NAME, KEY_IS_ENABLED }, null, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns all ringer names in the database, where or not if they are enabled
|
||||
* @return list of all strings in the database table
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public List<String> getAllLocationNames() {
|
||||
List<String> list = new ArrayList<String>();
|
||||
Cursor cursor = this.mDb.query(LOCATION_TABLE, new String[] { KEY_LOCATION_NAME }, null, null, null, null, null);
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
list.add(cursor.getString(0));
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
if (cursor != null && !cursor.isClosed()) {
|
||||
cursor.close();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* gets a ringer from a row id;
|
||||
* @param id
|
||||
* @return cursor containing the note
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public Cursor getRingerFromId(long id) {
|
||||
return this.mDb.query(LOCATION_TABLE, new String[]{ KEY_LOCATION_NAME, KEY_IS_ENABLED }, "id = "+id, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* gets a ringer's info from the supplied ringer name
|
||||
* @param ringerName
|
||||
* @return
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public ContentValues getLocationInfo(String ringerName){
|
||||
ContentValues values = new ContentValues();
|
||||
Cursor info = this.mDb.query(LOCATION_INFO_TABLE, new String[]{ KEY, KEY_VALUE }, KEY_LOCATION_NAME +" = "+ DatabaseUtils.sqlEscapeString(ringerName), null, null, null, null);
|
||||
if (info.moveToFirst()) {
|
||||
do {
|
||||
values.put(info.getString(0), info.getString(1));
|
||||
} while (info.moveToNext());
|
||||
}
|
||||
if (info != null && !info.isClosed()) {
|
||||
info.close();
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ringer's name form the ringer table
|
||||
* @param id
|
||||
* @return ringer's name
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public String getLocationName(long id) {
|
||||
String name = null;
|
||||
Cursor cursor = this.mDb.query(LOCATION_TABLE, new String[]{ KEY_LOCATION_NAME }, "id = "+id, null, null, null, null);;
|
||||
if (cursor.moveToFirst()) {
|
||||
name = cursor.getString(0);
|
||||
}
|
||||
if (cursor != null && !cursor.isClosed()) {
|
||||
cursor.close();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a new ringer into the database
|
||||
* @param ringer values
|
||||
* @param ringerInfo values
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public void insertRinger(ContentValues ringer, ContentValues ringerInfo){
|
||||
ringer.put(Database.KEY_LOCATION_NAME, checkName(ringer.getAsString(Database.KEY_LOCATION_NAME)));
|
||||
mDb.insert(LOCATION_TABLE, null, ringer);
|
||||
String ringerName = ringer.getAsString(Database.KEY_LOCATION_NAME);
|
||||
|
||||
//insert the information values
|
||||
for(Entry<String, Object> item : ringerInfo.valueSet()){
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(KEY_LOCATION_NAME, ringerName);
|
||||
values.put(KEY, item.getKey());
|
||||
/*
|
||||
* Try get the value.
|
||||
* If there is a class cast exception, try casting to the next object type.
|
||||
*
|
||||
* The following types are tried:
|
||||
* String
|
||||
* Integer
|
||||
* Boolean
|
||||
*/
|
||||
try {
|
||||
values.put(KEY_VALUE, (String) item.getValue());
|
||||
} catch (ClassCastException e) {
|
||||
try {
|
||||
values.put(KEY_VALUE, (Boolean) item.getValue() ? 1 : 0);
|
||||
} catch (ClassCastException e1) {
|
||||
values.put(KEY_VALUE, (Integer) item.getValue());
|
||||
}
|
||||
}
|
||||
mDb.insert(LOCATION_INFO_TABLE, null, values);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if a ringer is enabled
|
||||
* @param row id
|
||||
* @return true if the ringer is enabled
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public boolean isRingerEnabled(long id) {
|
||||
Cursor cursor = this.mDb.query(LOCATION_TABLE, new String[] { KEY_IS_ENABLED }, "id = "+id, null, null, null, null);
|
||||
if (cursor.moveToFirst()) {
|
||||
if(Debug.DEBUG)
|
||||
Log.d(TAG, "isRingerEnabled("+id+") = "+ cursor.getString(0));
|
||||
return parseBoolean(cursor.getString(0));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the database from the sdcard
|
||||
* @return true if successful
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public void restore(){
|
||||
File dbFile = new File(Environment.getDataDirectory() + "/data/"+mContext.getPackageName()+"/databases/"+DATABASE_NAME);
|
||||
|
||||
File exportDir = new File(Environment.getExternalStorageDirectory(), "/"+this.mContext.getString(R.string.app_name));
|
||||
if (!exportDir.exists()) {
|
||||
exportDir.mkdirs();
|
||||
}
|
||||
File file = new File(exportDir, dbFile.getName());
|
||||
|
||||
try {
|
||||
file.createNewFile();
|
||||
this.copyFile(file, dbFile);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
/*
|
||||
* close and reopen the database to upgrade it.
|
||||
*/
|
||||
this.mDb.close();
|
||||
this.mDb = new OpenHelper(this.mContext).getWritableDatabase();
|
||||
if(this.mDb.isOpen() && ! this.isUpgrading)
|
||||
if(this.mListener != null)
|
||||
this.mListener.onRestoreComplete();
|
||||
}
|
||||
|
||||
public int setLocationEnabled(long id, boolean enabled) {
|
||||
if(Debug.DEBUG)
|
||||
Log.d(TAG, "setRingerEnabled("+id+") = "+ enabled);
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(KEY_IS_ENABLED, enabled);
|
||||
return mDb.update(LOCATION_TABLE, values, "id" + "= "+ id, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* updates a ringer by it's id
|
||||
* @param id
|
||||
* @param ringer values
|
||||
* @param info values
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public void updateLocation(long id, ContentValues ringer, ContentValues info) throws NullPointerException{
|
||||
|
||||
if(ringer == null || info == null)
|
||||
throw new NullPointerException("ringer content was null");
|
||||
|
||||
String ringer_name = getLocationName(id);
|
||||
|
||||
if(!ringer_name.equals(ringer.getAsString(Database.KEY_LOCATION_NAME)))
|
||||
ringer.put(Database.KEY_LOCATION_NAME, checkName(ringer.getAsString(Database.KEY_LOCATION_NAME)));
|
||||
|
||||
//update the information values in the info table
|
||||
for(Entry<String, Object> item : info.valueSet()){
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(KEY_LOCATION_NAME, ringer.getAsString(KEY_LOCATION_NAME));
|
||||
values.put(KEY, item.getKey());
|
||||
try {
|
||||
values.put(KEY_VALUE, (String) item.getValue());
|
||||
} catch (ClassCastException e) {
|
||||
try {
|
||||
values.put(KEY_VALUE, (Boolean) item.getValue() ? 1 : 0);
|
||||
} catch (ClassCastException e1) {
|
||||
values.put(KEY_VALUE, (Integer) item.getValue());
|
||||
}
|
||||
}
|
||||
//try to update, if update fails insert
|
||||
if(!(mDb.update(LOCATION_INFO_TABLE, values, KEY_LOCATION_NAME + "="+ DatabaseUtils.sqlEscapeString(ringer_name) +" AND " + KEY +"='"+ item.getKey()+"'", null) > 0))
|
||||
mDb.insert(LOCATION_INFO_TABLE, null, values);
|
||||
}
|
||||
|
||||
//update the ringer table
|
||||
mDb.update(LOCATION_TABLE, ringer, "id" + "= "+ id, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the row ids after a row is deleted
|
||||
* @param id of the row to start with
|
||||
* @author ricky barrette
|
||||
*/
|
||||
private void updateRowIds(long id) {
|
||||
long currentRow;
|
||||
ContentValues values = new ContentValues();
|
||||
Cursor cursor = this.mDb.query(LOCATION_TABLE, new String[] { "id" },null, null, null, null, null);
|
||||
if (cursor.moveToFirst()) {
|
||||
do {
|
||||
currentRow = cursor.getLong(0);
|
||||
if(currentRow == id){
|
||||
id++;
|
||||
values.clear();
|
||||
values.put("id", currentRow -1);
|
||||
mDb.update(LOCATION_TABLE, values, "id" + "= "+ currentRow, null);
|
||||
}
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
if (cursor != null && !cursor.isClosed()) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* OnDatabaseUpgradeCompeteListener.java
|
||||
* @date Nov 10, 2011
|
||||
* @author Twenty Codes, LLC
|
||||
* @author ricky barrette
|
||||
*/
|
||||
package com.TwentyCodes.android.FindMyCarLib.db;
|
||||
|
||||
/**
|
||||
* This interface will be used to listen to see when the database events are complete
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public interface DatabaseListener {
|
||||
|
||||
public void onDatabaseUpgradeComplete();
|
||||
|
||||
public void onLocationDeletionComplete();
|
||||
|
||||
public void onRestoreComplete();
|
||||
|
||||
public void onDatabaseUpgrade();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Debug.java
|
||||
* @date Mar 24, 2011
|
||||
* @author ricky barrette
|
||||
* @author Twenty Codes, LLC
|
||||
*/
|
||||
package com.TwentyCodes.android.FindMyCarLib.debug;
|
||||
|
||||
/**
|
||||
* A Convince class to hold constant variables to force test situations
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public class Debug {
|
||||
|
||||
public static final boolean DEBUG = false;
|
||||
|
||||
/**
|
||||
* When set to true, this will force the first boot dialog to be displayed at every boot
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public static final boolean FORCE_FIRSTBOOT_DIALOG = false;
|
||||
|
||||
/**
|
||||
* Drops the ringer database table every time the database is created
|
||||
*/
|
||||
public static boolean DROP_TABLE_EVERY_TIME = false;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* FragmentAdapter.java
|
||||
* @date Aug 6, 2011
|
||||
* @author Twenty Codes, LLC
|
||||
* @author ricky barrette
|
||||
*/
|
||||
package com.jakewharton.android.viewpagerindicator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentPagerAdapter;
|
||||
|
||||
/**
|
||||
* This adaptor maintains the How and What fragments
|
||||
* @author ricky
|
||||
*/
|
||||
class FragmentAdapter extends FragmentPagerAdapter {
|
||||
|
||||
private ArrayList<Fragment> mFragments;
|
||||
|
||||
/**
|
||||
* Creates a new FragmentAdaptor
|
||||
* @param fm
|
||||
* @param fragments to be displayed
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public FragmentAdapter(FragmentManager fm, ArrayList<Fragment> fragments) {
|
||||
super(fm);
|
||||
this.mFragments = fragments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
return this.mFragments.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return this.mFragments.size();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Patrik Akerfeldt
|
||||
* Copyright (C) 2011 Jake Wharton
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.jakewharton.android.viewpagerindicator;
|
||||
|
||||
import android.support.v4.view.ViewPager;
|
||||
|
||||
/**
|
||||
* A PageIndicator is responsible to show an visual indicator on the total views
|
||||
* number and the current visible view.
|
||||
*/
|
||||
public interface PageIndicator extends ViewPager.OnPageChangeListener {
|
||||
/**
|
||||
* Bind the indicator to a ViewPager.
|
||||
*
|
||||
* @param view
|
||||
*/
|
||||
public void setViewPager(ViewPager view);
|
||||
|
||||
/**
|
||||
* Bind the indicator to a ViewPager.
|
||||
*
|
||||
* @param view
|
||||
* @param initialPosition
|
||||
*/
|
||||
public void setViewPager(ViewPager view, int initialPosition);
|
||||
|
||||
/**
|
||||
* <p>Set the current page of both the ViewPager and indicator.</p>
|
||||
*
|
||||
* <p>This <strong>must</strong> be used if you need to set the page before
|
||||
* the views are drawn on screen (e.g., default start page).</p>
|
||||
*
|
||||
* @param item
|
||||
*/
|
||||
public void setCurrentItem(int item);
|
||||
|
||||
/**
|
||||
* Set a page change listener which will receive forwarded events.
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener);
|
||||
}
|
||||
@@ -0,0 +1,640 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Patrik Akerfeldt
|
||||
* Copyright (C) 2011 Francisco Figueiredo Jr.
|
||||
* Copyright (C) 2011 Jake Wharton
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jakewharton.android.viewpagerindicator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.TwentyCodes.android.exception.R;
|
||||
|
||||
/**
|
||||
* A TitlePageIndicator is a PageIndicator which displays the title of left view
|
||||
* (if exist), the title of the current select view (centered) and the title of
|
||||
* the right view (if exist). When the user scrolls the ViewPager then titles are
|
||||
* also scrolled.
|
||||
*/
|
||||
public class TitlePageIndicator extends TextView implements PageIndicator, View.OnTouchListener {
|
||||
private static final float UNDERLINE_FADE_PERCENTAGE = 0.25f;
|
||||
|
||||
public enum IndicatorStyle {
|
||||
None(0), Triangle(1), Underline(2);
|
||||
|
||||
public final int value;
|
||||
|
||||
private IndicatorStyle(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static IndicatorStyle fromValue(int value) {
|
||||
for (IndicatorStyle style : IndicatorStyle.values()) {
|
||||
if (style.value == value) {
|
||||
return style;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ViewPager mViewPager;
|
||||
private ViewPager.OnPageChangeListener mListener;
|
||||
private TitleProvider mTitleProvider;
|
||||
private int mCurrentPage;
|
||||
private int mCurrentOffset;
|
||||
private final Paint mPaintText;
|
||||
private final Paint mPaintSelected;
|
||||
private Path mPath;
|
||||
private final Paint mPaintFooterLine;
|
||||
private IndicatorStyle mFooterIndicatorStyle;
|
||||
private final Paint mPaintFooterIndicator;
|
||||
private float mFooterIndicatorHeight;
|
||||
private float mFooterIndicatorPadding;
|
||||
private float mFooterIndicatorUnderlinePadding;
|
||||
private float mTitlePadding;
|
||||
/** Left and right side padding for not active view titles. */
|
||||
private float mClipPadding;
|
||||
private float mFooterLineHeight;
|
||||
|
||||
|
||||
public TitlePageIndicator(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TitlePageIndicator(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, R.attr.titlePageIndicatorStyle);
|
||||
}
|
||||
|
||||
public TitlePageIndicator(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
super.setOnTouchListener(this);
|
||||
|
||||
//Load defaults from resources
|
||||
final Resources res = getResources();
|
||||
final int defaultFooterColor = res.getColor(R.color.default_title_indicator_footer_color);
|
||||
final float defaultFooterLineHeight = res.getDimension(R.dimen.default_title_indicator_footer_line_height);
|
||||
final int defaultFooterIndicatorStyle = res.getInteger(R.integer.default_title_indicator_footer_indicator_style);
|
||||
final float defaultFooterIndicatorHeight = res.getDimension(R.dimen.default_title_indicator_footer_indicator_height);
|
||||
final float defaultFooterIndicatorPadding = res.getDimension(R.dimen.default_title_indicator_footer_indicator_padding);
|
||||
final float defaultFooterIndicatorUnderlinePadding = res.getDimension(R.dimen.default_title_indicator_footer_indicator_underline_padding);
|
||||
final int defaultSelectedColor = res.getColor(R.color.default_title_indicator_selected_color);
|
||||
final boolean defaultSelectedBold = res.getBoolean(R.bool.default_title_indicator_selected_bold);
|
||||
final int defaultTextColor = res.getColor(R.color.default_title_indicator_text_color);
|
||||
final float defaultTextSize = res.getDimension(R.dimen.default_title_indicator_text_size);
|
||||
final float defaultTitlePadding = res.getDimension(R.dimen.default_title_indicator_title_padding);
|
||||
final float defaultClipPadding = res.getDimension(R.dimen.default_title_indicator_clip_padding);
|
||||
|
||||
//Retrieve styles attributes
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TitlePageIndicator, defStyle, R.style.Widget_TitlePageIndicator);
|
||||
|
||||
//Retrieve the colors to be used for this view and apply them.
|
||||
mFooterLineHeight = a.getDimension(R.styleable.TitlePageIndicator_footerLineHeight, defaultFooterLineHeight);
|
||||
mFooterIndicatorStyle = IndicatorStyle.fromValue(a.getInteger(R.styleable.TitlePageIndicator_footerIndicatorStyle, defaultFooterIndicatorStyle));
|
||||
mFooterIndicatorHeight = a.getDimension(R.styleable.TitlePageIndicator_footerIndicatorHeight, defaultFooterIndicatorHeight);
|
||||
mFooterIndicatorPadding = a.getDimension(R.styleable.TitlePageIndicator_footerIndicatorPadding, defaultFooterIndicatorPadding);
|
||||
mFooterIndicatorUnderlinePadding = a.getDimension(R.styleable.TitlePageIndicator_footerIndicatorUnderlinePadding, defaultFooterIndicatorUnderlinePadding);
|
||||
mTitlePadding = a.getDimension(R.styleable.TitlePageIndicator_titlePadding, defaultTitlePadding);
|
||||
mClipPadding = a.getDimension(R.styleable.TitlePageIndicator_clipPadding, defaultClipPadding);
|
||||
|
||||
final float textSize = a.getDimension(R.styleable.TitlePageIndicator_textSize, defaultTextSize);
|
||||
final int footerColor = a.getColor(R.styleable.TitlePageIndicator_footerColor, defaultFooterColor);
|
||||
mPaintText = new Paint();
|
||||
mPaintText.setColor(a.getColor(R.styleable.TitlePageIndicator_textColor, defaultTextColor));
|
||||
mPaintText.setTextSize(textSize);
|
||||
mPaintText.setAntiAlias(true);
|
||||
mPaintSelected = new Paint();
|
||||
mPaintSelected.setColor(a.getColor(R.styleable.TitlePageIndicator_selectedColor, defaultSelectedColor));
|
||||
mPaintSelected.setTextSize(textSize);
|
||||
mPaintSelected.setFakeBoldText(a.getBoolean(R.styleable.TitlePageIndicator_selectedBold, defaultSelectedBold));
|
||||
mPaintSelected.setAntiAlias(true);
|
||||
mPaintFooterLine = new Paint();
|
||||
mPaintFooterLine.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
mPaintFooterLine.setStrokeWidth(mFooterLineHeight);
|
||||
mPaintFooterLine.setColor(footerColor);
|
||||
mPaintFooterIndicator = new Paint();
|
||||
mPaintFooterIndicator.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
mPaintFooterIndicator.setColor(footerColor);
|
||||
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
|
||||
public int getFooterColor() {
|
||||
return mPaintFooterLine.getColor();
|
||||
}
|
||||
|
||||
public void setFooterColor(int footerColor) {
|
||||
mPaintFooterLine.setColor(footerColor);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public float getFooterLineHeight() {
|
||||
return mFooterLineHeight;
|
||||
}
|
||||
|
||||
public void setFooterLineHeight(float footerLineHeight) {
|
||||
mFooterLineHeight = footerLineHeight;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public float getFooterIndicatorHeight() {
|
||||
return mFooterIndicatorHeight;
|
||||
}
|
||||
|
||||
public void setFooterIndicatorHeight(float footerTriangleHeight) {
|
||||
mFooterIndicatorHeight = footerTriangleHeight;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public IndicatorStyle getFooterIndicatorStyle() {
|
||||
return mFooterIndicatorStyle;
|
||||
}
|
||||
|
||||
public void setFooterIndicatorStyle(IndicatorStyle indicatorStyle) {
|
||||
mFooterIndicatorStyle = indicatorStyle;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public int getSelectedColor() {
|
||||
return mPaintSelected.getColor();
|
||||
}
|
||||
|
||||
public void setSelectedColor(int selectedColor) {
|
||||
mPaintSelected.setColor(selectedColor);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public boolean isSelectedBold() {
|
||||
return mPaintSelected.isFakeBoldText();
|
||||
}
|
||||
|
||||
public void setSelectedBold(boolean selectedBold) {
|
||||
mPaintSelected.setFakeBoldText(selectedBold);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public int getTextColor() {
|
||||
return mPaintText.getColor();
|
||||
}
|
||||
|
||||
public void setTextColor(int textColor) {
|
||||
mPaintText.setColor(textColor);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public float getTextSize() {
|
||||
return mPaintText.getTextSize();
|
||||
}
|
||||
|
||||
public void setTextSize(float textSize) {
|
||||
mPaintText.setTextSize(textSize);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public float getTitlePadding() {
|
||||
return this.mTitlePadding;
|
||||
}
|
||||
|
||||
public void setTitlePadding(float titlePadding) {
|
||||
mTitlePadding = titlePadding;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public float getClipPadding() {
|
||||
return this.mClipPadding;
|
||||
}
|
||||
|
||||
public void setClipPadding(float clipPadding) {
|
||||
mClipPadding = clipPadding;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see android.view.View#onDraw(android.graphics.Canvas)
|
||||
*/
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
//Calculate views bounds
|
||||
ArrayList<Rect> bounds = calculateAllBounds(mPaintText);
|
||||
|
||||
final int count = mViewPager.getAdapter().getCount();
|
||||
final int countMinusOne = count - 1;
|
||||
final int halfWidth = getWidth() / 2;
|
||||
final int left = getLeft();
|
||||
final int width = getWidth();
|
||||
final int height = getHeight();
|
||||
final int leftPlusWidth = left + width;
|
||||
|
||||
//Verify if the current view must be clipped to the screen
|
||||
Rect curViewBound = bounds.get(mCurrentPage);
|
||||
int curViewWidth = curViewBound.right - curViewBound.left;
|
||||
if (curViewBound.left < 0) {
|
||||
//Try to clip to the screen (left side)
|
||||
clipViewOnTheLeft(curViewBound, curViewWidth);
|
||||
}
|
||||
if (curViewBound.right > leftPlusWidth) {
|
||||
//Try to clip to the screen (right side)
|
||||
clipViewOnTheRight(curViewBound, curViewWidth, leftPlusWidth);
|
||||
}
|
||||
|
||||
//Left views starting from the current position
|
||||
if (mCurrentPage > 0) {
|
||||
for (int i = mCurrentPage - 1; i >= 0; i--) {
|
||||
Rect bound = bounds.get(i);
|
||||
int w = bound.right - bound.left;
|
||||
//Is left side is outside the screen
|
||||
if (bound.left < 0) {
|
||||
//Try to clip to the screen (left side)
|
||||
clipViewOnTheLeft(bound, w);
|
||||
//Except if there's an intersection with the right view
|
||||
if (i < countMinusOne && mCurrentPage != i) {
|
||||
Rect rightBound = bounds.get(i + 1);
|
||||
//Intersection
|
||||
if (bound.right + (int)mTitlePadding > rightBound.left) {
|
||||
bound.left = rightBound.left - (w + (int)mTitlePadding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//Right views starting from the current position
|
||||
if (mCurrentPage < countMinusOne) {
|
||||
for (int i = mCurrentPage + 1 ; i < count; i++) {
|
||||
Rect bound = bounds.get(i);
|
||||
int w = bound.right - bound.left;
|
||||
//If right side is outside the screen
|
||||
if (bound.right > leftPlusWidth) {
|
||||
//Try to clip to the screen (right side)
|
||||
clipViewOnTheRight(bound, w, leftPlusWidth);
|
||||
//Except if there's an intersection with the left view
|
||||
if (i > 0 && mCurrentPage != i) {
|
||||
Rect leftBound = bounds.get(i - 1);
|
||||
//Intersection
|
||||
if (bound.left - (int)mTitlePadding < leftBound.right) {
|
||||
bound.left = leftBound.right + (int)mTitlePadding;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bitmap icon = null;
|
||||
//Now draw views
|
||||
for (int i = 0; i < count; i++) {
|
||||
//Get the title
|
||||
Rect bound = bounds.get(i);
|
||||
//Only if one side is visible
|
||||
if ((bound.left > left && bound.left < leftPlusWidth) || (bound.right > left && bound.right < leftPlusWidth)) {
|
||||
Paint paint = mPaintText;
|
||||
//Change the color is the title is closed to the center
|
||||
int middle = (bound.left + bound.right) / 2;
|
||||
if (Math.abs(middle - halfWidth) < 20) {
|
||||
paint = mPaintSelected;
|
||||
}
|
||||
|
||||
/*
|
||||
* Draw the icons
|
||||
* @author ricky barrette
|
||||
*/
|
||||
icon = BitmapFactory.decodeResource( getResources(), mTitleProvider.getIcon(i));;
|
||||
canvas.drawBitmap(icon, bound.left - (icon.getWidth() + 4), (bound.bottom - (icon.getHeight() / 2) )-mFooterIndicatorHeight , paint);
|
||||
canvas.drawText(mTitleProvider.getTitle(i), bound.left, bound.bottom, paint);
|
||||
}
|
||||
}
|
||||
|
||||
//Draw the footer line
|
||||
mPath = new Path();
|
||||
mPath.moveTo(0, height - mFooterLineHeight);
|
||||
mPath.lineTo(width, height - mFooterLineHeight);
|
||||
mPath.close();
|
||||
canvas.drawPath(mPath, mPaintFooterLine);
|
||||
|
||||
switch (mFooterIndicatorStyle) {
|
||||
case Triangle:
|
||||
mPath = new Path();
|
||||
mPath.moveTo(halfWidth, height - mFooterLineHeight - mFooterIndicatorHeight);
|
||||
mPath.lineTo(halfWidth + mFooterIndicatorHeight, height - mFooterLineHeight);
|
||||
mPath.lineTo(halfWidth - mFooterIndicatorHeight, height - mFooterLineHeight);
|
||||
mPath.close();
|
||||
canvas.drawPath(mPath, mPaintFooterIndicator);
|
||||
break;
|
||||
|
||||
case Underline:
|
||||
float deltaPercentage = mCurrentOffset * 1.0f / width;
|
||||
int alpha = 0xFF;
|
||||
int page = mCurrentPage;
|
||||
if (deltaPercentage <= UNDERLINE_FADE_PERCENTAGE) {
|
||||
alpha = (int)(0xFF * ((UNDERLINE_FADE_PERCENTAGE - deltaPercentage) / UNDERLINE_FADE_PERCENTAGE));
|
||||
} else if (deltaPercentage >= (1 - UNDERLINE_FADE_PERCENTAGE)) {
|
||||
alpha = (int)(0xFF * ((deltaPercentage - (1 - UNDERLINE_FADE_PERCENTAGE)) / UNDERLINE_FADE_PERCENTAGE));
|
||||
page += 1; //We are coming into the next page
|
||||
} else if (mCurrentOffset != 0) {
|
||||
break; //Not in underline scope
|
||||
}
|
||||
|
||||
Rect underlineBounds = bounds.get(page);
|
||||
mPath = new Path();
|
||||
mPath.moveTo(underlineBounds.left - mFooterIndicatorUnderlinePadding, height - mFooterLineHeight);
|
||||
mPath.lineTo(underlineBounds.right + mFooterIndicatorUnderlinePadding, height - mFooterLineHeight);
|
||||
mPath.lineTo(underlineBounds.right + mFooterIndicatorUnderlinePadding, height - mFooterLineHeight - mFooterIndicatorHeight);
|
||||
mPath.lineTo(underlineBounds.left - mFooterIndicatorUnderlinePadding, height - mFooterLineHeight - mFooterIndicatorHeight);
|
||||
mPath.close();
|
||||
|
||||
mPaintFooterIndicator.setAlpha(alpha);
|
||||
canvas.drawPath(mPath, mPaintFooterIndicator);
|
||||
mPaintFooterIndicator.setAlpha(0xFF);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean onTouch(View view, MotionEvent event) {
|
||||
if ((view != this) || (event.getAction() != MotionEvent.ACTION_DOWN)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int count = mViewPager.getAdapter().getCount();
|
||||
final float halfWidth = getWidth() / 2;
|
||||
final float sixthWidth = getWidth() / 6;
|
||||
|
||||
if ((mCurrentPage > 0) && (event.getX() < halfWidth - sixthWidth)) {
|
||||
mViewPager.setCurrentItem(mCurrentPage - 1);
|
||||
return true;
|
||||
} else if ((mCurrentPage < count - 1) && (event.getX() > halfWidth + sixthWidth)) {
|
||||
mViewPager.setCurrentItem(mCurrentPage + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setOnTouchListener(OnTouchListener listener) {
|
||||
throw new UnsupportedOperationException("This view does not support listening to its touch events.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set bounds for the right textView including clip padding.
|
||||
*
|
||||
* @param curViewBound
|
||||
* current bounds.
|
||||
* @param curViewWidth
|
||||
* width of the view.
|
||||
*/
|
||||
private void clipViewOnTheRight(Rect curViewBound, int curViewWidth, int leftPlusWidth) {
|
||||
curViewBound.right = leftPlusWidth - (int)mClipPadding;
|
||||
curViewBound.left = curViewBound.right - curViewWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set bounds for the left textView including clip padding.
|
||||
*
|
||||
* @param curViewBound
|
||||
* current bounds.
|
||||
* @param curViewWidth
|
||||
* width of the view.
|
||||
*/
|
||||
private void clipViewOnTheLeft(Rect curViewBound, int curViewWidth) {
|
||||
curViewBound.left = 0 + (int)mClipPadding;
|
||||
curViewBound.right = curViewWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate views bounds and scroll them according to the current index
|
||||
*
|
||||
* @param paint
|
||||
* @param currentIndex
|
||||
* @return
|
||||
*/
|
||||
private ArrayList<Rect> calculateAllBounds(Paint paint) {
|
||||
ArrayList<Rect> list = new ArrayList<Rect>();
|
||||
//For each views (If no values then add a fake one)
|
||||
final int count = mViewPager.getAdapter().getCount();
|
||||
final int width = getWidth();
|
||||
final int halfWidth = width / 2;
|
||||
for (int i = 0; i < count; i++) {
|
||||
Rect bounds = calcBounds(i, paint);
|
||||
int w = (bounds.right - bounds.left);
|
||||
int h = (bounds.bottom - bounds.top);
|
||||
bounds.left = (halfWidth) - (w / 2) - mCurrentOffset + ((i - mCurrentPage) * width);
|
||||
bounds.right = bounds.left + w;
|
||||
bounds.top = 0;
|
||||
bounds.bottom = h;
|
||||
list.add(bounds);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the bounds for a view's title
|
||||
*
|
||||
* @param index
|
||||
* @param paint
|
||||
* @return
|
||||
*/
|
||||
private Rect calcBounds(int index, Paint paint) {
|
||||
//Calculate the text bounds
|
||||
Rect bounds = new Rect();
|
||||
bounds.right = (int)paint.measureText(mTitleProvider.getTitle(index));
|
||||
bounds.bottom = (int)(paint.descent() - paint.ascent());
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setViewPager(ViewPager view) {
|
||||
if (view.getAdapter() == null) {
|
||||
throw new IllegalStateException("ViewPager does not have adapter instance.");
|
||||
}
|
||||
if (!(view.getAdapter() instanceof TitleProvider)) {
|
||||
throw new IllegalStateException("ViewPager adapter must implement TitleProvider to be used with TitlePageIndicator.");
|
||||
}
|
||||
mViewPager = view;
|
||||
mViewPager.setOnPageChangeListener(this);
|
||||
mTitleProvider = (TitleProvider)mViewPager.getAdapter();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setViewPager(ViewPager view, int initialPosition) {
|
||||
setViewPager(view);
|
||||
setCurrentItem(initialPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentItem(int item) {
|
||||
if (mViewPager == null) {
|
||||
throw new IllegalStateException("ViewPager has not been bound.");
|
||||
}
|
||||
mViewPager.setCurrentItem(item);
|
||||
mCurrentPage = item;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
if (mListener != null) {
|
||||
mListener.onPageScrollStateChanged(state);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
mCurrentPage = position;
|
||||
mCurrentOffset = positionOffsetPixels;
|
||||
invalidate();
|
||||
|
||||
if (mListener != null) {
|
||||
mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
if (mListener != null) {
|
||||
mListener.onPageSelected(position);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see android.view.View#onMeasure(int, int)
|
||||
*/
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the width of this view
|
||||
*
|
||||
* @param measureSpec
|
||||
* A measureSpec packed into an int
|
||||
* @return The width of the view, honoring constraints from measureSpec
|
||||
*/
|
||||
private int measureWidth(int measureSpec) {
|
||||
int result = 0;
|
||||
int specMode = MeasureSpec.getMode(measureSpec);
|
||||
int specSize = MeasureSpec.getSize(measureSpec);
|
||||
|
||||
if (specMode != MeasureSpec.EXACTLY) {
|
||||
throw new IllegalStateException(getClass().getSimpleName() + " can only be used in EXACTLY mode.");
|
||||
}
|
||||
result = specSize;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the height of this view
|
||||
*
|
||||
* @param measureSpec
|
||||
* A measureSpec packed into an int
|
||||
* @return The height of the view, honoring constraints from measureSpec
|
||||
*/
|
||||
private int measureHeight(int measureSpec) {
|
||||
float result = 0;
|
||||
int specMode = MeasureSpec.getMode(measureSpec);
|
||||
int specSize = MeasureSpec.getSize(measureSpec);
|
||||
|
||||
if (specMode == MeasureSpec.EXACTLY) {
|
||||
//We were told how big to be
|
||||
result = specSize;
|
||||
} else {
|
||||
//Calculate the text bounds
|
||||
Rect bounds = new Rect();
|
||||
bounds.bottom = (int) (mPaintText.descent()-mPaintText.ascent());
|
||||
result = bounds.bottom - bounds.top + mFooterLineHeight;
|
||||
if (mFooterIndicatorStyle != IndicatorStyle.None) {
|
||||
result += mFooterIndicatorHeight + mFooterIndicatorPadding;
|
||||
}
|
||||
}
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
SavedState savedState = (SavedState)state;
|
||||
super.onRestoreInstanceState(savedState.getSuperState());
|
||||
mCurrentPage = savedState.currentPage;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
setFreezesText(true);
|
||||
Parcelable superState = super.onSaveInstanceState();
|
||||
SavedState savedState = new SavedState(superState);
|
||||
savedState.currentPage = mCurrentPage;
|
||||
return savedState;
|
||||
}
|
||||
|
||||
static class SavedState extends BaseSavedState {
|
||||
int currentPage;
|
||||
|
||||
public SavedState(Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
private SavedState(Parcel in) {
|
||||
super(in);
|
||||
currentPage = in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeInt(currentPage);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
|
||||
@Override
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Patrik Akerfeldt
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jakewharton.android.viewpagerindicator;
|
||||
|
||||
/**
|
||||
* A TitleProvider provides the title to display according to a view.
|
||||
*/
|
||||
public interface TitleProvider {
|
||||
/**
|
||||
* Returns the title of the view at position
|
||||
* @param position
|
||||
* @return
|
||||
*/
|
||||
public String getTitle(int position);
|
||||
|
||||
/**
|
||||
* returns the icon res id of the view at position
|
||||
* @param postion
|
||||
* @return
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public int getIcon(int postion);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* TitleFragmentAdapter.java
|
||||
* @date Aug 6, 2011
|
||||
* @author Twenty Codes, LLC
|
||||
* @author ricky barrette
|
||||
*/
|
||||
package com.jakewharton.android.viewpagerindicator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
|
||||
|
||||
/**
|
||||
* This adaptor maintains a ViewPager title indicator.
|
||||
* @author ricky
|
||||
*/
|
||||
public class TitledFragmentAdapter extends FragmentAdapter implements TitleProvider {
|
||||
|
||||
private String[] mTitles;
|
||||
private int[] mIcons;
|
||||
|
||||
/**
|
||||
* Creates a new TitleFragmentAdapter
|
||||
* @param fm
|
||||
* @param fragments to be displayed
|
||||
* @param titles for the fragments
|
||||
* @author ricky barrette
|
||||
*/
|
||||
public TitledFragmentAdapter(FragmentManager fm, ArrayList<Fragment> fragments, String[] titles, int[] icons) {
|
||||
super(fm, fragments);
|
||||
this.mTitles = titles;
|
||||
this.mIcons = icons;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle(int position) {
|
||||
return this.mTitles[position];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIcon(int position) {
|
||||
return this.mIcons[position];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user