intial commit of 4.0

This commit is contained in:
2011-12-10 17:29:14 +00:00
parent 937f12c2b0
commit 15124addce
209 changed files with 7896 additions and 0 deletions

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View 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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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>()));
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -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");
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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);
}

View File

@@ -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];
}
};
}
}

View File

@@ -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);
}

View File

@@ -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];
}
}