From 9a2d59929e445f6592022c3524e7bc08ef8d50c0 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Thu, 6 Sep 2012 18:19:45 -0500 Subject: [PATCH 01/68] Fixing spelling on build params --- app/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pom.xml b/app/pom.xml index b01296e..ec08cc4 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -150,7 +150,7 @@ Also, if you have a ci (Continuous Integration) profile, these values can be passed in via the build server. --> ${bootstrap.sign.keystore} - ${boostrap.sign.alias} + ${bootstrap.sign.alias} ${bootstrap.sign.storepass} ${bootstrap.sign.keypass} true From 081f944e2447e838a86f762b231a3fad2b0682e4 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Thu, 6 Sep 2012 20:53:06 -0500 Subject: [PATCH 02/68] Updating pom and readme --- README.md | 7 +++++++ app/pom.xml | 2 +- integration-tests/pom.xml | 2 +- pom.xml | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 393dac5..17cff32 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,13 @@ to report any bugs or feature requests and to see the list of known issues. +## Authentication +Log into this demo app with the following credentials: + +user: demo@androidbootstrap.com + +password: android + ## License * [Apache Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html) diff --git a/app/pom.xml b/app/pom.xml index ec08cc4..3fb1450 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -10,7 +10,7 @@ https://github.com/donnfelker/android-bootstrap - 1.4 + 1.0 com.donnfelker.android.bootstrap android-bootstrap-parent diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 518cb4e..183072f 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -9,7 +9,7 @@ apk - 1.4 + 1.0 com.donnfelker.android.bootstrap android-bootstrap-parent diff --git a/pom.xml b/pom.xml index 6bdaf11..bdb209d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 - 1.4 + 1.0 com.donnfelker.android.bootstrap android-bootstrap-parent Android Bootstrap parent From 7587282029f7e71b0b56fe505950c822b4057aba Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Tue, 11 Sep 2012 09:52:47 -0700 Subject: [PATCH 03/68] Readme edits --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 17cff32..dcc47e2 100644 --- a/README.md +++ b/README.md @@ -77,9 +77,9 @@ and uses many great open-source libraries from the Android dev community: * [ActionBarSherlock](https://github.com/JakeWharton/ActionBarSherlock) for a consistent, great looking header across all Android platforms, [ViewPagerIndicator](https://github.com/JakeWharton/Android-ViewPagerIndicator) - for swiping between content, traffic, & referrer pages, and - [NineOldAndroids](https://github.com/JakeWharton/NineOldAndroids) for the - AirTraffic view animations - all from [Jake Wharton](http://jakewharton.com/). + for swiping between fragments and + [NineOldAndroids](https://github.com/JakeWharton/NineOldAndroids) for + view animations - all from [Jake Wharton](http://jakewharton.com/). * [RoboGuice](http://code.google.com/p/roboguice/) for dependency-injection. * [Robotium](http://code.google.com/p/robotium/) for driving our app during integration tests. From 0d74378a239455d0fcabd56db9f6228afb1d4297 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Tue, 11 Sep 2012 10:04:05 -0700 Subject: [PATCH 04/68] Adding instructions for bootstrapping with the generator. --- README.md | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dcc47e2..2da6541 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,10 @@ This repository contains the source code for the [Android Bootstrap](http://www.androidbootstrap.com/) Android app available soon from [Google Play](https://play.google.com/store/apps/details?id=com.donnfelker.android.bootstrap). - - Please see the [issues](https://github.com/donnfelker/android-bootstrap/issues) section to report any bugs or feature requests and to see the list of known issues. - + @@ -19,6 +17,30 @@ user: demo@androidbootstrap.com password: android + +## Generating your Bootstrap App +Why generate? Simple ... renaming files, folders, copy and pasting is SUPER error prone and well... it sucks overall. This can easily take a few days with debugging if you run into issues and perform a lot of typo's. Using the generator on [AndroidBootstrap.com](http://www.androidbootstrap.com) you can generate your application with your application name as well as the package (and folder structure) that you want to work with. + +As an example, you know that you want your app name and package to the following: + + - *App Name*: Notify + - *Package Name*: com.notify.app.mobile + +After generating the app on [AndroidBootstrap.com](http://www.androidbootstrap.com) the folder structure of the source code for the app will change: + + - From: __com/donnfelker/android/bootstrap__ + - To: __com/notify/app/mobile__ + +At that point all the source files that were located in ____com/donnfelker/android/bootstrap__ will be moved to the new folder __com/notify/app/mobile__. + +All import statments that reference the old resources (__R.com.donnfelker.android.bootstrap.R__) will now be renamed to the correct package. The artifact id's in the *pom.xml* (and various other places) will be replaced. The App Name will be replaced in the strings/etc. + +The end result is that you will be given a zip file with the correct structure. Open the zip and then execute *mvn clean package* and your app should be ready for development. + +Enjoy! + +The application + ## License * [Apache Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html) From 855a1e3e1e112911e8e4236aa40b78f8eb31c6d0 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Tue, 11 Sep 2012 11:08:58 -0700 Subject: [PATCH 05/68] Updating readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2da6541..de4730a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Android Bootstrap App This repository contains the source code for the [Android Bootstrap](http://www.androidbootstrap.com/) -Android app available soon from [Google Play](https://play.google.com/store/apps/details?id=com.donnfelker.android.bootstrap). +Android app available from [Google Play](https://play.google.com/store/apps/details?id=com.donnfelker.android.bootstrap). Please see the [issues](https://github.com/donnfelker/android-bootstrap/issues) section to report any bugs or feature requests and to see the list of known issues. From 931b3f5078d3d0f802ba714926d2d8b516fb7d06 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Wed, 26 Sep 2012 08:24:16 -0700 Subject: [PATCH 06/68] Adding link to discussion forum --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index de4730a..cab6c56 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Android app available from [Google Play](https://play.google.com/store/apps/deta Please see the [issues](https://github.com/donnfelker/android-bootstrap/issues) section to report any bugs or feature requests and to see the list of known issues. +Have a questions about Android Bootstrap? Ask away on the [android-bootstrap discussion forum](https://groups.google.com/forum/#!forum/android-bootstrap). + From a812c4656e4d431f23e88d27e31315883a6e8d24 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Wed, 3 Oct 2012 09:17:57 -0700 Subject: [PATCH 07/68] Fixing import statement. --- .../android/bootstrap/ui/NewsActivity.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsActivity.java index bac4b8c..db30d3f 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsActivity.java @@ -1,23 +1,15 @@ package com.donnfelker.android.bootstrap.ui; -import android.content.Intent; +import static com.donnfelker.android.bootstrap.core.Constants.Extra.NEWS_ITEM; import android.os.Bundle; import android.widget.TextView; -import com.actionbarsherlock.R; -import com.actionbarsherlock.view.MenuItem; -import com.donnfelker.android.bootstrap.BootstrapServiceProvider; +import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.core.News; -import com.github.rtyley.android.sherlock.roboguice.activity.RoboSherlockActivity; -import com.google.inject.Inject; import roboguice.inject.InjectExtra; import roboguice.inject.InjectView; -import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; -import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP; -import static com.donnfelker.android.bootstrap.core.Constants.Extra.NEWS_ITEM; - public class NewsActivity extends BootstrapActivity { @InjectExtra(NEWS_ITEM) protected News newsItem; From e6bb99df3d8595eaae22811d43a9185082169044 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Wed, 3 Oct 2012 09:34:40 -0700 Subject: [PATCH 08/68] Fixing target in default.properties. Fixes #12 --- default.properties | 2 +- integration-tests/default.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/default.properties b/default.properties index a923a3f..37b6a1a 100644 --- a/default.properties +++ b/default.properties @@ -1,4 +1,4 @@ # A dummy file to force the Android Emulator Plugin to install SDK 15 # which we need for compiling against ABS 4. -target=android-15 +target=android-16 diff --git a/integration-tests/default.properties b/integration-tests/default.properties index 31ff39a..ad4ae37 100644 --- a/integration-tests/default.properties +++ b/integration-tests/default.properties @@ -1,4 +1,4 @@ # Project target. -target=android-15 +target=android-16 From 9069e4085bdbd3de82584c1668e9ac322a7e1164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Marek?= Date: Thu, 18 Oct 2012 12:27:45 +0300 Subject: [PATCH 09/68] Upgrade ActionBarSherlock to 4.2.0 --- app/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pom.xml b/app/pom.xml index 3fb1450..863d818 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -16,7 +16,7 @@ - 4.1.0 + 4.2.0 From 80d6d9a480c9df0f8dfc27be865e20e00c5093bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Marek?= Date: Thu, 18 Oct 2012 12:29:25 +0300 Subject: [PATCH 10/68] absForceOverflow doesn't work with ABS 4.2.0 --- app/res/values/theme.xml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/res/values/theme.xml b/app/res/values/theme.xml index e330dd9..0689dbd 100644 --- a/app/res/values/theme.xml +++ b/app/res/values/theme.xml @@ -1,11 +1,9 @@ - + + + + \ No newline at end of file diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java index e8c9fd0..85a04ea 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java @@ -9,6 +9,10 @@ import android.content.Context; import com.github.kevinsawicki.http.HttpRequest; +import com.google.inject.Injector; +import com.google.inject.Stage; + +import roboguice.RoboGuice; /** * Android Bootstrap application @@ -34,6 +38,13 @@ public BootstrapApplication(final Context context) { attachBaseContext(context); } + @Override + public void onCreate() { + super.onCreate(); + + setApplicationInjector(this); + } + /** * Create main application * @@ -43,4 +54,15 @@ public BootstrapApplication(final Instrumentation instrumentation) { this(); attachBaseContext(instrumentation.getTargetContext()); } + + /** + * Sets the application injector. Using the {@link RoboGuice#newDefaultRoboModule} as well as a + * custom binding module {@link BootstrapModule} to set up your application module + * @param application + * @return + */ + public static Injector setApplicationInjector(Application application) { + return RoboGuice.setBaseApplicationInjector(application, Stage.DEVELOPMENT, RoboGuice.newDefaultRoboModule + (application), new BootstrapModule()); + } } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java new file mode 100644 index 0000000..c36b28e --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java @@ -0,0 +1,21 @@ +package com.donnfelker.android.bootstrap; + +import com.google.inject.AbstractModule; +import com.google.inject.Singleton; +import com.squareup.otto.Bus; + +/** + * Module for setting up custom bindings in RoboGuice. + */ +public class BootstrapModule extends AbstractModule { + + @Override + protected void configure() { + + // We want Otto to be bound as a singleton as one instance only needs + // to be present in this app + bind(Bus.class).in(Singleton.class); + + } + +} diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/Constants.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/Constants.java index 81954ce..cd5a386 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/core/Constants.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/Constants.java @@ -97,6 +97,12 @@ private Intent() {} } + public static class Notification{ + private Notification() {} + + public static final int TIMER_NOTIFICATION_ID = 1000; // Why 1000? Why not? :) + } + } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/PauseTimerEvent.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/PauseTimerEvent.java new file mode 100644 index 0000000..bd98769 --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/PauseTimerEvent.java @@ -0,0 +1,7 @@ +package com.donnfelker.android.bootstrap.core; + +/** + * Marker class for Otto for a pause event for the timer. + */ +public class PauseTimerEvent { +} diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/ResumeTimerEvent.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/ResumeTimerEvent.java new file mode 100644 index 0000000..c767c71 --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/ResumeTimerEvent.java @@ -0,0 +1,7 @@ +package com.donnfelker.android.bootstrap.core; + +/** + * Marker class for resuming a timer through Otto + */ +public class ResumeTimerEvent { +} diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/StopTimerEvent.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/StopTimerEvent.java new file mode 100644 index 0000000..672e2c4 --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/StopTimerEvent.java @@ -0,0 +1,7 @@ +package com.donnfelker.android.bootstrap.core; + +/** + * Marker class for the stop timer event in Otto. + */ +public class StopTimerEvent { +} diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerPausedEvent.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerPausedEvent.java new file mode 100644 index 0000000..83104c1 --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerPausedEvent.java @@ -0,0 +1,14 @@ +package com.donnfelker.android.bootstrap.core; + +public class TimerPausedEvent { + + private boolean timerIsPaused; + + public TimerPausedEvent(boolean timerIsPaused) { + this.timerIsPaused = timerIsPaused; + } + + public boolean isTimerIsPaused() { + return timerIsPaused; + } +} diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java new file mode 100644 index 0000000..a62e5fb --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java @@ -0,0 +1,209 @@ +package com.donnfelker.android.bootstrap.core; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.SystemClock; +import android.support.v4.app.NotificationCompat; + +import com.donnfelker.android.bootstrap.R; +import com.donnfelker.android.bootstrap.ui.BootstrapTimerActivity; +import com.google.inject.Inject; +import com.squareup.otto.Bus; +import com.squareup.otto.Produce; +import com.squareup.otto.Subscribe; + +import roboguice.service.RoboService; +import roboguice.util.Ln; + +import static com.donnfelker.android.bootstrap.core.Constants.Notification.TIMER_NOTIFICATION_ID; + +public class TimerService extends RoboService { + + @Inject protected Bus BUS; + @Inject private NotificationManager notificationManager; + + private boolean timerRunning = false; + private boolean timerStarted; + private long base; + private long currentRunningTimeInMillis; + private long pausedBaseTime; + private boolean isPaused; + + public static final int TICK_WHAT = 2; + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + super.onCreate(); + + // Register the bus so we can send notifications. + BUS.register(this); + + } + + @Override + public void onDestroy() { + + // Unregister bus, since its not longer needed as the service is shutting down + BUS.unregister(this); + + notificationManager.cancel(TIMER_NOTIFICATION_ID); + + Ln.d("Service has been destroyed"); + + super.onDestroy(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + + if(timerStarted == false) { + + timerStarted = true; + + startTimer(); + + // Run as foreground service: http://stackoverflow.com/a/3856940/5210 + // Another example: https://github.com/commonsguy/cw-android/blob/master/Notifications/FakePlayer/src/com/commonsware/android/fakeplayerfg/PlayerService.java + startForeground(TIMER_NOTIFICATION_ID, getNotification(getString(R.string.timer_running))); + } + + return START_NOT_STICKY; + } + + @Produce + public TimerTickEvent produceTickEvent() { + return new TimerTickEvent(currentRunningTimeInMillis); + } + + @Produce + public TimerPausedEvent produceTimerIsPausedEvent() { + return new TimerPausedEvent(isPaused); + } + + @Subscribe + public void onStopEvent(StopTimerEvent stopEvent) { + + timerHandler.removeMessages(TICK_WHAT); + stopSelf(); + } + + @Subscribe + public void onPauseEvent(PauseTimerEvent pauseEvent) { + pauseTimer(); + } + + /** + * Pauses the active running timer and updates the notification in the status bar. + */ + private void pauseTimer() { + + updateNotification(getString(R.string.timer_is_paused)); + + timerHandler.removeMessages(TICK_WHAT); + pausedBaseTime = SystemClock.elapsedRealtime() - base; + timerRunning = false; + isPaused = true; + + produceTimerIsPausedEvent(); + } + + @Subscribe + public void onResumeTimerEvent(ResumeTimerEvent resumeTimerEvent) { + startTimer(); + } + + private void startTimer() { + startChronoTimer(); + notifyTimerRunning(); + } + + private void startChronoTimer() { + base = SystemClock.elapsedRealtime(); + + // If coming from a paused state, then find our true base. + if(pausedBaseTime > 0) + base = base - pausedBaseTime; + + isPaused = false; + + updateRunning(); + } + + /** + * Starts the generic timer. + */ + private void updateRunning() { + if (timerStarted != timerRunning) { + if (timerStarted) { + dispatchTimerUpdate(SystemClock.elapsedRealtime()); + timerHandler.sendMessageDelayed(Message.obtain(timerHandler, TICK_WHAT), 1000); + } else { + timerHandler.removeMessages(TICK_WHAT); + } + timerRunning = timerStarted; + } + } + + private Handler timerHandler = new Handler() { + public void handleMessage(Message m) { + if (timerRunning) { + dispatchTimerUpdate(SystemClock.elapsedRealtime()); + sendMessageDelayed(Message.obtain(this, TICK_WHAT), 1000); + } + } + }; + + private void dispatchTimerUpdate(long now) { + + currentRunningTimeInMillis = now - base; + Ln.d("Elapsed Seconds: " + currentRunningTimeInMillis / 1000); + + BUS.post(produceTickEvent()); + + } + + + + private void notifyTimerRunning() { + updateNotification(getString(R.string.timer_running)); + produceTimerIsPausedEvent(); + } + + + private void updateNotification(String message) { + notificationManager.notify(TIMER_NOTIFICATION_ID, getNotification(message)); + + } + + /** + * Creates a notification to show in the notification bar + * @param message the message to display in the notification bar + * @return a new {@link Notification} + */ + private Notification getNotification(String message) { + final Intent i = new Intent(this, BootstrapTimerActivity.class); + + PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i, 0); + + return new NotificationCompat.Builder(this) + .setContentTitle(getString(R.string.app_name)) + .setSmallIcon(R.drawable.ic_stat_ab_notification) + .setContentText(message) + .setAutoCancel(false) + .setOnlyAlertOnce(true) + .setOngoing(true) + .setWhen(System.currentTimeMillis()) + .setContentIntent(pendingIntent) + .getNotification(); + } +} diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerTickEvent.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerTickEvent.java new file mode 100644 index 0000000..056c343 --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerTickEvent.java @@ -0,0 +1,35 @@ +package com.donnfelker.android.bootstrap.core; + + +/** + * Event used to pass tick events around through the message bus. + * This is mainly used in the {@link BootstrapTimer} to show the updates on the timer + * as the background service runs the timer. + */ +public class TimerTickEvent { + private final long millis; + + public TimerTickEvent(long millis) { + this.millis = millis; + } + + public long getMillis() { + return millis; + } + + public long getSeconds() { + return (millis / 1000); + } + + + @Override + public String toString() { + return new StringBuilder("") + .append("Millis: " + getMillis()) + .append(", ") + .append("Seconds: " + getSeconds()) + .toString(); + } + +} + diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java new file mode 100644 index 0000000..e5d261a --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java @@ -0,0 +1,231 @@ +package com.donnfelker.android.bootstrap.ui; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +import com.donnfelker.android.bootstrap.R; +import com.donnfelker.android.bootstrap.core.PauseTimerEvent; +import com.donnfelker.android.bootstrap.core.ResumeTimerEvent; +import com.donnfelker.android.bootstrap.core.StopTimerEvent; +import com.donnfelker.android.bootstrap.core.TimerPausedEvent; +import com.donnfelker.android.bootstrap.core.TimerService; +import com.donnfelker.android.bootstrap.core.TimerTickEvent; +import com.github.rtyley.android.sherlock.roboguice.activity.RoboSherlockFragmentActivity; +import com.google.inject.Inject; +import com.squareup.otto.Bus; +import com.squareup.otto.Subscribe; + +import roboguice.inject.InjectView; + +public class BootstrapTimerActivity extends RoboSherlockFragmentActivity implements View.OnClickListener { + + @Inject protected Bus BUS; + + @InjectView(R.id.chronometer) protected TextView chronometer; + @InjectView(R.id.start) protected Button start; + @InjectView(R.id.stop) protected Button stop; + @InjectView(R.id.pause) protected Button pause; + @InjectView(R.id.resume) protected Button resume; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.bootstrap_timer); + + setTitle(R.string.timer); + + start.setOnClickListener(this); + stop.setOnClickListener(this); + pause.setOnClickListener(this); + resume.setOnClickListener(this); + + } + + @Override + protected void onResume() { + super.onResume(); + + BUS.register(this); + } + + @Override + protected void onPause() { + super.onPause(); + + BUS.unregister(this); + } + + @Override + public void onClick(View v) { + switch(v.getId()) { + case R.id.start: + startTimer(); + break; + case R.id.stop: + produceStopEvent(); + break; + case R.id.pause: + producePauseEvent(); + break; + case R.id.resume: + produceResumeEvent(); + break; + } + } + + /** + * Starts the timer service + */ + private void startTimer() { + if(isTimerServiceRunning() == false) { + final Intent i = new Intent(this, TimerService.class); + startService(i); + + start.setVisibility(View.GONE); + stop.setVisibility(View.VISIBLE); + pause.setVisibility(View.VISIBLE); + } + } + + /** + * Posts a {@link StopTimerEvent} message to the {@link Bus} + */ + private void produceStopEvent() { + BUS.post(new StopTimerEvent()); + } + + /** + * Posts a {@link PauseTimerEvent} message to the {@link Bus} + */ + private void producePauseEvent() { + BUS.post(new PauseTimerEvent()); + } + + /** + * Posts a {@link ResumeTimerEvent} message to the {@link Bus} + */ + private void produceResumeEvent() { + BUS.post(new ResumeTimerEvent()); + } + + @Subscribe + public void onTimerPausedEvent(TimerPausedEvent event) { + if(event.isTimerIsPaused()) { + resume.setVisibility(View.VISIBLE); + stop.setVisibility(View.VISIBLE); + pause.setVisibility(View.GONE); + start.setVisibility(View.GONE); + } else if(isTimerServiceRunning()) { + pause.setVisibility(View.VISIBLE); + stop.setVisibility(View.VISIBLE); + resume.setVisibility(View.GONE); + start.setVisibility(View.GONE); + } + } + + /** + * Called by {@link Bus} when a tick event occurs. + * @param event The event + */ + @Subscribe + public void onTickEvent(TimerTickEvent event) { + setFormattedTime(event.getMillis()); + } + + + + /** + * Called by {@link Bus} when a tick event occurs. + * @param event The event + */ + @Subscribe + public void onPauseEvent(PauseTimerEvent event) { + resume.setVisibility(View.VISIBLE); + pause.setVisibility(View.GONE); + } + + /** + * Called by {@link Bus} when a tick event occurs. + * @param event The event + */ + @Subscribe + public void onResumeEvent(ResumeTimerEvent event) { + resume.setVisibility(View.GONE); + pause.setVisibility(View.VISIBLE); + } + + /** + * Called by {@link Bus} when a tick event occurs. + * @param event The event + */ + @Subscribe + public void onStopEvent(StopTimerEvent event) { + resume.setVisibility(View.GONE); + pause.setVisibility(View.GONE); + start.setVisibility(View.VISIBLE); + stop.setVisibility(View.GONE); + setFormattedTime(0); // Since its stopped, zero out the timer. + } + + /** + * Checks to see if the timer service is running or not. + * @return true if the service is running otherwise false. + */ + private boolean isTimerServiceRunning() { + ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); + for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { + if (TimerService.class.getName().equals(service.service.getClassName())) { + return true; + } + } + return false; + } + + /** + * Sets the formatted time + * @param millis the elapsed time + */ + private void setFormattedTime(long millis) { + final String formattedTime = formatTime(millis); + chronometer.setText(formattedTime); + } + + /** + * Formats the time to look like "HH:MM:SS" + * @param millis The number of elapsed milliseconds + * @return A formatted time value + */ + public static String formatTime(long millis) { + + long seconds = millis / 1000; + long minutes = seconds / 60; + long hours = minutes / 60; + + seconds = seconds % 60; + minutes = minutes % 60; + hours = hours % 60; + + String secondsD = String.valueOf(seconds); + String minutesD = String.valueOf(minutes); + String hoursD = String.valueOf(hours); + + if (seconds < 10) + secondsD = "0" + seconds; + if (minutes < 10) + minutesD = "0" + minutes; + if (hours < 10) + hoursD = "0" + hours; + + // HH:MM:SS + return String.format("%1$s:%2$s:%3$s" , hoursD , minutesD , secondsD); + + } + + +} diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java index a5b6745..f750349 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java @@ -8,6 +8,7 @@ import android.os.Bundle; import android.support.v4.view.ViewPager; +import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.Window; import com.donnfelker.android.bootstrap.R; @@ -39,5 +40,15 @@ protected void onCreate(Bundle savedInstanceState) { pager.setCurrentItem(1); } - + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch(item.getItemId()) { + case id.timer: + final Intent i = new Intent(this, BootstrapTimerActivity.class); + startActivity(i); + return true; + default: + return super.onOptionsItemSelected(item); + } + } } diff --git a/proguard.cfg b/proguard.cfg index a1ee567..d59730c 100644 --- a/proguard.cfg +++ b/proguard.cfg @@ -71,3 +71,8 @@ java.lang.Object writeReplace(); java.lang.Object readResolve(); } + +-keepclassmembers class ** { + @com.squareup.otto.Subscribe public *; + @com.squareup.otto.Produce public *; +} From 253a01edb130c385303fd0ffa4603405aa4413be Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Sat, 9 Mar 2013 15:08:46 -0700 Subject: [PATCH 26/68] Adding dagger to pom --- app/pom.xml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/pom.xml b/app/pom.xml index fea8539..6ab4fd4 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -17,6 +17,7 @@ 4.2.0 + 0.9 @@ -40,6 +41,17 @@ roboguice 2.0 + + com.squareup + dagger + ${dagger.version} + + + com.squareup + dagger-compiler + ${dagger.version} + true + com.actionbarsherlock library @@ -109,6 +121,13 @@ com.jayway.maven.plugins.android.generation2 android-maven-plugin + + + com.squareup + dagger-compiler + ${dagger.version} + + maven-compiler-plugin From 5f86baceeebe5cf12d12014f5ba90039dbaaa923 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Sat, 9 Mar 2013 15:41:28 -0700 Subject: [PATCH 27/68] Updating activities, Adding Strings, Ln and SafeAsyncTask as well as updating InjectExtra to normal getExtra call. Also cleaned up various imports and added new SherlockAccountAuthenticatorActivity from rtyley source of roboguice-sherlock. --- app/pom.xml | 15 +- .../bootstrap/BootstrapApplication.java | 20 +- .../bootstrap/BootstrapServiceProvider.java | 2 +- .../authenticator/ApiKeyProvider.java | 2 +- .../BootstrapAuthenticatorActivity.java | 27 +- .../authenticator/LogoutService.java | 14 +- .../SherlockAccountAuthenticatorActivity.java | 86 +++++ .../android/bootstrap/core/AvatarLoader.java | 8 +- .../android/bootstrap/core/TimerService.java | 10 +- .../bootstrap/core/UserAgentProvider.java | 10 +- .../bootstrap/ui/BootstrapActivity.java | 14 +- .../ui/BootstrapFragmentActivity.java | 19 ++ .../bootstrap/ui/BootstrapTimerActivity.java | 7 +- .../bootstrap/ui/CarouselActivity.java | 6 +- .../bootstrap/ui/CheckInsListAdapter.java | 3 - .../bootstrap/ui/CheckInsListFragment.java | 3 +- .../bootstrap/ui/ItemListFragment.java | 8 +- .../android/bootstrap/ui/NewsActivity.java | 9 +- .../bootstrap/ui/NewsListFragment.java | 2 +- .../android/bootstrap/ui/ThrowableLoader.java | 3 +- .../android/bootstrap/ui/UserActivity.java | 13 +- .../bootstrap/ui/UserListFragment.java | 2 +- .../donnfelker/android/bootstrap/util/Ln.java | 284 +++++++++++++++++ .../android/bootstrap/util/SafeAsyncTask.java | 296 ++++++++++++++++++ .../android/bootstrap/util/Strings.java | 181 +++++++++++ 25 files changed, 949 insertions(+), 95 deletions(-) create mode 100644 app/src/main/java/com/donnfelker/android/bootstrap/authenticator/SherlockAccountAuthenticatorActivity.java create mode 100644 app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java create mode 100644 app/src/main/java/com/donnfelker/android/bootstrap/util/Ln.java create mode 100644 app/src/main/java/com/donnfelker/android/bootstrap/util/SafeAsyncTask.java create mode 100644 app/src/main/java/com/donnfelker/android/bootstrap/util/Strings.java diff --git a/app/pom.xml b/app/pom.xml index 6ab4fd4..5696d20 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -31,16 +31,6 @@ gson 2.2.2 - - com.github.rtyley - roboguice-sherlock - 1.5 - - - org.roboguice - roboguice - 2.0 - com.squareup dagger @@ -52,6 +42,11 @@ ${dagger.version} true + + com.jakewharton + butterknife + 1.2.0 + com.actionbarsherlock library diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java index 85a04ea..11f3295 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java @@ -9,10 +9,6 @@ import android.content.Context; import com.github.kevinsawicki.http.HttpRequest; -import com.google.inject.Injector; -import com.google.inject.Stage; - -import roboguice.RoboGuice; /** * Android Bootstrap application @@ -38,12 +34,7 @@ public BootstrapApplication(final Context context) { attachBaseContext(context); } - @Override - public void onCreate() { - super.onCreate(); - setApplicationInjector(this); - } /** * Create main application @@ -55,14 +46,5 @@ public BootstrapApplication(final Instrumentation instrumentation) { attachBaseContext(instrumentation.getTargetContext()); } - /** - * Sets the application injector. Using the {@link RoboGuice#newDefaultRoboModule} as well as a - * custom binding module {@link BootstrapModule} to set up your application module - * @param application - * @return - */ - public static Injector setApplicationInjector(Application application) { - return RoboGuice.setBaseApplicationInjector(application, Stage.DEVELOPMENT, RoboGuice.newDefaultRoboModule - (application), new BootstrapModule()); - } + } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapServiceProvider.java b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapServiceProvider.java index 6be04fd..779d945 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapServiceProvider.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapServiceProvider.java @@ -6,7 +6,7 @@ import com.donnfelker.android.bootstrap.authenticator.ApiKeyProvider; import com.donnfelker.android.bootstrap.core.BootstrapService; import com.donnfelker.android.bootstrap.core.UserAgentProvider; -import com.google.inject.Inject; +import javax.inject.Inject; import java.io.IOException; diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/ApiKeyProvider.java b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/ApiKeyProvider.java index 500a6f0..1b9f1a0 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/ApiKeyProvider.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/ApiKeyProvider.java @@ -11,7 +11,7 @@ import android.os.Bundle; import com.donnfelker.android.bootstrap.core.Constants; -import com.google.inject.Inject; +import javax.inject.Inject; import java.io.IOException; diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAuthenticatorActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAuthenticatorActivity.java index 84d5627..93eb4a4 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAuthenticatorActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAuthenticatorActivity.java @@ -40,23 +40,22 @@ import com.donnfelker.android.bootstrap.core.Constants; import com.donnfelker.android.bootstrap.core.User; +import com.donnfelker.android.bootstrap.util.Ln; +import com.donnfelker.android.bootstrap.util.SafeAsyncTask; +import com.donnfelker.android.bootstrap.util.Strings; import com.github.kevinsawicki.http.HttpRequest; import com.github.kevinsawicki.wishlist.Toaster; import com.donnfelker.android.bootstrap.R.id; import com.donnfelker.android.bootstrap.R.layout; import com.donnfelker.android.bootstrap.R.string; import com.donnfelker.android.bootstrap.ui.TextWatcherAdapter; -import com.github.rtyley.android.sherlock.roboguice.activity.RoboSherlockAccountAuthenticatorActivity; import com.google.gson.Gson; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; -import roboguice.inject.InjectView; -import roboguice.util.Ln; -import roboguice.util.RoboAsyncTask; -import roboguice.util.Strings; +import butterknife.InjectView; import static com.donnfelker.android.bootstrap.core.Constants.Http.USERNAME; import static com.donnfelker.android.bootstrap.core.Constants.Http.PASSWORD; @@ -64,8 +63,7 @@ /** * Activity to authenticate the user against an API (example API on Parse.com) */ -public class BootstrapAuthenticatorActivity extends - RoboSherlockAccountAuthenticatorActivity { +public class BootstrapAuthenticatorActivity extends SherlockAccountAuthenticatorActivity { /** * PARAM_CONFIRMCREDENTIALS @@ -90,18 +88,13 @@ public class BootstrapAuthenticatorActivity extends private AccountManager accountManager; - @InjectView(id.et_email) - private AutoCompleteTextView emailText; - - @InjectView(id.et_password) - private EditText passwordText; - - @InjectView(id.b_signin) - private Button signinButton; + @InjectView(id.et_email) private AutoCompleteTextView emailText; + @InjectView(id.et_password) private EditText passwordText; + @InjectView(id.b_signin) private Button signinButton; private TextWatcher watcher = validationTextWatcher(); - private RoboAsyncTask authenticationTask; + private SafeAsyncTask authenticationTask; private String authToken; private String authTokenType; @@ -241,7 +234,7 @@ public void handleLogin(View view) { password = passwordText.getText().toString(); showProgress(); - authenticationTask = new RoboAsyncTask(this) { + authenticationTask = new SafeAsyncTask() { public Boolean call() throws Exception { final String query = String.format("%s=%s&%s=%s", PARAM_USERNAME, email, PARAM_PASSWORD, password); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/LogoutService.java b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/LogoutService.java index f7139a9..1e0003b 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/LogoutService.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/LogoutService.java @@ -7,15 +7,16 @@ import android.util.Log; import com.donnfelker.android.bootstrap.core.Constants; -import com.google.inject.Inject; +import com.donnfelker.android.bootstrap.util.SafeAsyncTask; + +import javax.inject.Inject; import java.util.concurrent.Callable; import java.util.concurrent.Executor; -import roboguice.inject.ContextSingleton; -import roboguice.util.RoboAsyncTask; -@ContextSingleton +// TODO-dagger: Mark this a s singleton of some sort, maybe. +// @ContextSingleton public class LogoutService { @Inject protected Context context; @@ -28,12 +29,13 @@ public void logout(final Runnable onSuccess) { new LogoutTask(context, onSuccess).execute(); } - private static class LogoutTask extends RoboAsyncTask { + private static class LogoutTask extends SafeAsyncTask { + private final Context context; private Runnable onSuccess; protected LogoutTask(Context context, Runnable onSuccess) { - super(context); + this.context = context; this.onSuccess = onSuccess; } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/SherlockAccountAuthenticatorActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/SherlockAccountAuthenticatorActivity.java new file mode 100644 index 0000000..1dd168b --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/SherlockAccountAuthenticatorActivity.java @@ -0,0 +1,86 @@ +package com.donnfelker.android.bootstrap.authenticator; + +/* + * Copyright (C) 2009 The Android Open Source Project + * + * 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. + */ + +import android.accounts.AccountAuthenticatorResponse; +import android.accounts.AccountManager; +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import com.actionbarsherlock.app.SherlockActivity; + +/** + * Base class for implementing an Activity that is used to help implement an + * AbstractAccountAuthenticator. If the AbstractAccountAuthenticator needs to use an activity + * to handle the request then it can have the activity extend SherlockAccountAuthenticatorActivity. + * The AbstractAccountAuthenticator passes in the response to the intent using the following: + *
+ *      intent.putExtra({@link android.accounts.AccountManager#KEY_ACCOUNT_AUTHENTICATOR_RESPONSE}, response);
+ * 
+ * The activity then sets the result that is to be handed to the response via + * {@link #setAccountAuthenticatorResult(android.os.Bundle)}. + * This result will be sent as the result of the request when the activity finishes. If this + * is never set or if it is set to null then error {@link android.accounts.AccountManager#ERROR_CODE_CANCELED} + * will be called on the response. + */ +public class SherlockAccountAuthenticatorActivity extends SherlockActivity { + private AccountAuthenticatorResponse mAccountAuthenticatorResponse = null; + private Bundle mResultBundle = null; + + /** + * Set the result that is to be sent as the result of the request that caused this + * Activity to be launched. If result is null or this method is never called then + * the request will be canceled. + * @param result this is returned as the result of the AbstractAccountAuthenticator request + */ + public final void setAccountAuthenticatorResult(Bundle result) { + mResultBundle = result; + } + + /** + * Retreives the AccountAuthenticatorResponse from either the intent of the icicle, if the + * icicle is non-zero. + * @param icicle the save instance data of this Activity, may be null + */ + protected void onCreate(Bundle icicle) { + super.onCreate(icicle); + + mAccountAuthenticatorResponse = + getIntent().getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE); + + if (mAccountAuthenticatorResponse != null) { + mAccountAuthenticatorResponse.onRequestContinued(); + } + } + + /** + * Sends the result or a Constants.ERROR_CODE_CANCELED error if a result isn't present. + */ + public void finish() { + if (mAccountAuthenticatorResponse != null) { + // send the result bundle back if set, otherwise send an error. + if (mResultBundle != null) { + mAccountAuthenticatorResponse.onResult(mResultBundle); + } else { + mAccountAuthenticatorResponse.onError(AccountManager.ERROR_CODE_CANCELED, + "canceled"); + } + mAccountAuthenticatorResponse = null; + } + super.finish(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/AvatarLoader.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/AvatarLoader.java index 2d1a3b2..508e0d4 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/core/AvatarLoader.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/AvatarLoader.java @@ -14,8 +14,9 @@ import com.actionbarsherlock.app.ActionBar; import com.donnfelker.android.bootstrap.R; +import com.donnfelker.android.bootstrap.util.SafeAsyncTask; import com.github.kevinsawicki.http.HttpRequest; -import com.google.inject.Inject; +import javax.inject.Inject; import java.io.File; import java.io.FileOutputStream; @@ -26,7 +27,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; -import roboguice.util.RoboAsyncTask; /** * Avatar utilities @@ -40,13 +40,13 @@ public class AvatarLoader { private static final int CACHE_SIZE = 75; private static abstract class FetchAvatarTask extends - RoboAsyncTask { + SafeAsyncTask { private static final Executor EXECUTOR = Executors .newFixedThreadPool(1); private FetchAvatarTask(Context context) { - super(context, EXECUTOR); + super(EXECUTOR); } @Override diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java index a62e5fb..2bb633a 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java @@ -3,6 +3,7 @@ import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.IBinder; @@ -12,17 +13,16 @@ import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.ui.BootstrapTimerActivity; -import com.google.inject.Inject; +import javax.inject.Inject; + +import com.donnfelker.android.bootstrap.util.Ln; import com.squareup.otto.Bus; import com.squareup.otto.Produce; import com.squareup.otto.Subscribe; -import roboguice.service.RoboService; -import roboguice.util.Ln; - import static com.donnfelker.android.bootstrap.core.Constants.Notification.TIMER_NOTIFICATION_ID; -public class TimerService extends RoboService { +public class TimerService extends Service { @Inject protected Bus BUS; @Inject private NotificationManager notificationManager; diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/UserAgentProvider.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/UserAgentProvider.java index f6b1fb2..3dabda0 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/core/UserAgentProvider.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/UserAgentProvider.java @@ -6,15 +6,17 @@ import android.os.Build; import android.telephony.TelephonyManager; -import com.google.inject.Inject; -import com.google.inject.Provider; +import com.donnfelker.android.bootstrap.util.Ln; +import com.donnfelker.android.bootstrap.util.Strings; + +import javax.inject.Inject; +import javax.inject.Provider; + import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Locale; -import roboguice.util.Ln; -import roboguice.util.Strings; public class UserAgentProvider implements Provider { @Inject protected Application app; diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapActivity.java index 52be6dc..1771e14 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapActivity.java @@ -4,13 +4,23 @@ import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP; import android.content.Intent; +import com.actionbarsherlock.app.SherlockActivity; import com.actionbarsherlock.view.MenuItem; -import com.github.rtyley.android.sherlock.roboguice.activity.RoboSherlockActivity; + +import butterknife.Views; /** * Base activity for a Bootstrap activity which does not use fragments. */ -public abstract class BootstrapActivity extends RoboSherlockActivity { +public abstract class BootstrapActivity extends SherlockActivity { + + @Override + public void setContentView(int layoutResId) { + super.setContentView(layoutResId); + + // Used to inject views with the Butterknife library + Views.inject(this); + } @Override public boolean onOptionsItemSelected(MenuItem item) { diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java new file mode 100644 index 0000000..8082ae1 --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java @@ -0,0 +1,19 @@ +package com.donnfelker.android.bootstrap.ui; + +import com.actionbarsherlock.app.SherlockFragmentActivity; + +import butterknife.Views; + +/** + * Base class for all Bootstrap Activities that need fragments. + */ +public class BootstrapFragmentActivity extends SherlockFragmentActivity { + + @Override + public void setContentView(int layoutResId) { + super.setContentView(layoutResId); + + // Perform view injection via butter knife + Views.inject(this); + } +} diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java index e5d261a..7eaeadf 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java @@ -15,14 +15,13 @@ import com.donnfelker.android.bootstrap.core.TimerPausedEvent; import com.donnfelker.android.bootstrap.core.TimerService; import com.donnfelker.android.bootstrap.core.TimerTickEvent; -import com.github.rtyley.android.sherlock.roboguice.activity.RoboSherlockFragmentActivity; -import com.google.inject.Inject; +import javax.inject.Inject; import com.squareup.otto.Bus; import com.squareup.otto.Subscribe; -import roboguice.inject.InjectView; +import butterknife.InjectView; -public class BootstrapTimerActivity extends RoboSherlockFragmentActivity implements View.OnClickListener { +public class BootstrapTimerActivity extends BootstrapFragmentActivity implements View.OnClickListener { @Inject protected Bus BUS; diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java index f750349..c89f0d0 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java @@ -13,15 +13,15 @@ import com.actionbarsherlock.view.Window; import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.R.id; -import com.github.rtyley.android.sherlock.roboguice.activity.RoboSherlockFragmentActivity; + import com.viewpagerindicator.TitlePageIndicator; -import roboguice.inject.InjectView; +import butterknife.InjectView; /** * Activity to view the carousel and view pager indicator with fragments. */ -public class CarouselActivity extends RoboSherlockFragmentActivity { +public class CarouselActivity extends BootstrapFragmentActivity { @InjectView(id.tpi_header) private TitlePageIndicator indicator; @InjectView(id.vp_pages) private ViewPager pager; diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListAdapter.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListAdapter.java index c46e98e..2eacfac 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListAdapter.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListAdapter.java @@ -1,15 +1,12 @@ package com.donnfelker.android.bootstrap.ui; -import android.util.Log; import android.view.LayoutInflater; import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.core.CheckIn; -import com.donnfelker.android.bootstrap.core.News; import java.util.List; -import roboguice.util.Strings; public class CheckInsListAdapter extends AlternatingColorListAdapter { /** diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java index 0769ff8..0d676bf 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java @@ -13,8 +13,9 @@ import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.core.CheckIn; import com.github.kevinsawicki.wishlist.SingleTypeAdapter; -import com.google.inject.Inject; +import javax.inject.Inject; +import javax.inject.Inject; import java.util.List; public class CheckInsListFragment extends ItemListFragment { diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/ItemListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/ItemListFragment.java index 06370b6..d7fc22c 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/ItemListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/ItemListFragment.java @@ -17,6 +17,7 @@ import android.widget.ProgressBar; import android.widget.TextView; +import com.actionbarsherlock.app.SherlockFragment; import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; @@ -28,14 +29,13 @@ import com.donnfelker.android.bootstrap.R.id; import com.donnfelker.android.bootstrap.R.layout; import com.donnfelker.android.bootstrap.R.menu; -import com.github.rtyley.android.sherlock.roboguice.fragment.RoboSherlockFragment; -import com.google.inject.Inject; +import javax.inject.Inject; import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; -import roboguice.util.RoboAsyncTask; + /** * Base fragment for displaying a list of items that loads with a progress bar @@ -43,7 +43,7 @@ * * @param */ -public abstract class ItemListFragment extends RoboSherlockFragment +public abstract class ItemListFragment extends SherlockFragment implements LoaderCallbacks> { @Inject protected LogoutService logoutService; diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsActivity.java index db30d3f..5580fe1 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsActivity.java @@ -7,12 +7,11 @@ import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.core.News; -import roboguice.inject.InjectExtra; -import roboguice.inject.InjectView; +import butterknife.InjectView; public class NewsActivity extends BootstrapActivity { - @InjectExtra(NEWS_ITEM) protected News newsItem; + protected News newsItem; @InjectView(R.id.tv_title) protected TextView title; @InjectView(R.id.tv_content) protected TextView content; @@ -23,6 +22,10 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.news); + if(getIntent() != null && getIntent().getExtras() != null) { + newsItem = (News) getIntent().getExtras().getSerializable(NEWS_ITEM); + } + getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setHomeButtonEnabled(true); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java index 430fb73..34cf93a 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java @@ -13,7 +13,7 @@ import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.core.News; import com.github.kevinsawicki.wishlist.SingleTypeAdapter; -import com.google.inject.Inject; +import javax.inject.Inject; import java.util.List; diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/ThrowableLoader.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/ThrowableLoader.java index f4a6d61..ecdde3b 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/ThrowableLoader.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/ThrowableLoader.java @@ -4,7 +4,8 @@ import android.content.Context; import android.util.Log; -import roboguice.util.Ln; +import com.donnfelker.android.bootstrap.util.Ln; + /** * Loader that support throwing an exception when loading in the background diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserActivity.java index 86e7f37..44caaa1 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserActivity.java @@ -8,26 +8,29 @@ import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.core.AvatarLoader; import com.donnfelker.android.bootstrap.core.User; -import com.google.inject.Inject; +import javax.inject.Inject; -import roboguice.inject.InjectExtra; -import roboguice.inject.InjectView; +import butterknife.InjectView; public class UserActivity extends BootstrapActivity { @InjectView(R.id.iv_avatar) protected ImageView avatar; @InjectView(R.id.tv_name) protected TextView name; - @InjectExtra(USER) protected User user; - @Inject protected AvatarLoader avatarLoader; + protected User user; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.user_view); + if(getIntent() != null && getIntent().getExtras() != null) { + user = (User) getIntent().getExtras().getSerializable(USER); + } + getSupportActionBar().setHomeButtonEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java index ab53827..c527efe 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java @@ -16,7 +16,7 @@ import com.donnfelker.android.bootstrap.core.News; import com.donnfelker.android.bootstrap.core.User; import com.github.kevinsawicki.wishlist.SingleTypeAdapter; -import com.google.inject.Inject; +import javax.inject.Inject; import java.util.Collections; import java.util.List; diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/util/Ln.java b/app/src/main/java/com/donnfelker/android/bootstrap/util/Ln.java new file mode 100644 index 0000000..f32c825 --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/util/Ln.java @@ -0,0 +1,284 @@ +package com.donnfelker.android.bootstrap.util; + + +import android.app.Application; +import android.content.pm.ApplicationInfo; +import android.util.Log; + +import javax.inject.Inject; + + +/** + * Originally from RoboGuice: https://github.com/roboguice/roboguice/blob/master/roboguice/src/main/java/roboguice/util/Ln.java + * + * A more natural android logging facility. + * + * WARNING: CHECK OUT COMMON PITFALLS BELOW + * + * Unlike {@link android.util.Log}, Log provides sensible defaults. + * Debug and Verbose logging is enabled for applications that + * have "android:debuggable=true" in their AndroidManifest.xml. + * For apps built using SDK Tools r8 or later, this means any debug + * build. Release builds built with r8 or later will have verbose + * and debug log messages turned off. + * + * The default tag is automatically set to your app's packagename, + * and the current context (eg. activity, service, application, etc) + * is appended as well. You can add an additional parameter to the + * tag using {@link #Log(String)}. + * + * Log-levels can be programatically overridden for specific instances + * using {@link #Log(String, boolean, boolean)}. + * + * Log messages may optionally use {@link String#format(String, Object...)} + * formatting, which will not be evaluated unless the log statement is output. + * Additional parameters to the logging statement are treated as varrgs parameters + * to {@link String#format(String, Object...)} + * + * Also, the current file and line is automatically appended to the tag + * (this is only done if debug is enabled for performance reasons). + * + * COMMON PITFALLS: + * * Make sure you put the exception FIRST in the call. A common + * mistake is to place it last as is the android.util.Log convention, + * but then it will get treated as varargs parameter. + * * vararg parameters are not appended to the log message! You must + * insert them into the log message using %s or another similar + * format parameter + * + * Usage Examples: + * + * Ln.v("hello there"); + * Ln.d("%s %s", "hello", "there"); + * Ln.e( exception, "Error during some operation"); + * Ln.w( exception, "Error during %s operation", "some other"); + * + * + */ +@SuppressWarnings({"ImplicitArrayToString"}) +public class Ln { + /** + * config is initially set to BaseConfig() with sensible defaults, then replaced + * by BaseConfig(ContextSingleton) during guice static injection pass. + */ + @Inject protected static BaseConfig config = new BaseConfig(); + + /** + * print is initially set to Print(), then replaced by guice during + * static injection pass. This allows overriding where the log message is delivered to. + */ + @Inject protected static Print print = new Print(); + + + + private Ln() {} + + + + public static int v(Throwable t) { + return config.minimumLogLevel <= Log.VERBOSE ? print.println(Log.VERBOSE, Log.getStackTraceString(t)) : 0; + } + + public static int v(Object s1, Object... args) { + if( config.minimumLogLevel > Log.VERBOSE ) + return 0; + + final String s = Strings.toString(s1); + final String message = args.length>0 ? String.format(s,args) : s; + return print.println(Log.VERBOSE, message); + } + + public static int v(Throwable throwable, Object s1, Object... args ) { + if( config.minimumLogLevel > Log.VERBOSE ) + return 0; + + final String s = Strings.toString(s1); + final String message = (args.length>0 ? String.format(s,args) : s) + '\n' + Log.getStackTraceString(throwable); + return print.println(Log.VERBOSE, message); + } + + public static int d(Throwable t) { + return config.minimumLogLevel <= Log.DEBUG ? print.println(Log.DEBUG, Log.getStackTraceString(t)) : 0; + } + + public static int d(Object s1, Object... args) { + if( config.minimumLogLevel > Log.DEBUG ) + return 0; + + final String s = Strings.toString(s1); + final String message = args.length>0 ? String.format(s,args) : s; + return print.println(Log.DEBUG, message); + } + + public static int d(Throwable throwable, Object s1, Object... args) { + if( config.minimumLogLevel > Log.DEBUG ) + return 0; + + final String s = Strings.toString(s1); + final String message = (args.length>0 ? String.format(s,args) : s) + '\n' + Log.getStackTraceString(throwable); + return print.println(Log.DEBUG, message); + } + + public static int i(Throwable t) { + return config.minimumLogLevel <= Log.INFO ? print.println(Log.INFO, Log.getStackTraceString(t)) : 0; + } + + public static int i( Object s1, Object... args) { + if( config.minimumLogLevel > Log.INFO ) + return 0; + + final String s = Strings.toString(s1); + final String message = args.length>0 ? String.format(s,args) : s; + return print.println(Log.INFO, message); + } + + public static int i(Throwable throwable, Object s1, Object... args) { + if( config.minimumLogLevel > Log.INFO ) + return 0; + + final String s = Strings.toString(s1); + final String message = (args.length > 0 ? String.format(s, args) : s) + '\n' + Log.getStackTraceString(throwable); + return print.println(Log.INFO, message); + } + + public static int w(Throwable t) { + return config.minimumLogLevel <= Log.WARN ? print.println(Log.WARN, Log.getStackTraceString(t)) : 0; + } + + public static int w( Object s1, Object... args) { + if( config.minimumLogLevel > Log.WARN ) + return 0; + + final String s = Strings.toString(s1); + final String message = args.length>0 ? String.format(s,args) : s; + return print.println(Log.WARN, message); + } + + public static int w( Throwable throwable, Object s1, Object... args) { + if( config.minimumLogLevel > Log.WARN ) + return 0; + + final String s = Strings.toString(s1); + final String message = (args.length>0 ? String.format(s,args) : s) + '\n' + Log.getStackTraceString(throwable); + return print.println(Log.WARN, message); + } + + public static int e(Throwable t) { + return config.minimumLogLevel <= Log.ERROR ? print.println(Log.ERROR, Log.getStackTraceString(t)) : 0; + } + + public static int e( Object s1, Object... args) { + if( config.minimumLogLevel > Log.ERROR ) + return 0; + + final String s = Strings.toString(s1); + final String message = args.length>0 ? String.format(s,args) : s; + return print.println(Log.ERROR, message); + } + + public static int e( Throwable throwable, Object s1, Object... args) { + if( config.minimumLogLevel > Log.ERROR ) + return 0; + + final String s = Strings.toString(s1); + final String message = (args.length>0 ? String.format(s,args) : s) + '\n' + Log.getStackTraceString(throwable); + return print.println(Log.ERROR, message); + } + + public static boolean isDebugEnabled() { + return config.minimumLogLevel <= Log.DEBUG; + } + + public static boolean isVerboseEnabled() { + return config.minimumLogLevel <= Log.VERBOSE; + } + + public static Config getConfig() { + return config; + } + + + public static interface Config { + public int getLoggingLevel(); + public void setLoggingLevel(int level); + } + + public static class BaseConfig implements Config { + protected int minimumLogLevel = Log.VERBOSE; + protected String packageName = ""; + protected String scope = ""; + + protected BaseConfig() { + } + + @Inject + public BaseConfig(Application context) { + try { + packageName = context.getPackageName(); + final int flags = context.getPackageManager().getApplicationInfo(packageName, 0).flags; + minimumLogLevel = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0 ? Log.VERBOSE : Log.INFO; + scope = packageName.toUpperCase(); + + Ln.d("Configuring Logging, minimum log level is %s", logLevelToString(minimumLogLevel) ); + + } catch( Exception e ) { + try { + Log.e(packageName, "Error configuring logger", e); + } catch( RuntimeException f ) { + // HACK ignore Stub! errors in mock objects during testing + } + } + } + + public int getLoggingLevel() { + return minimumLogLevel; + } + + public void setLoggingLevel(int level) { + minimumLogLevel = level; + } + } + + public static String logLevelToString( int loglevel ) { + switch( loglevel ) { + case Log.VERBOSE: + return "VERBOSE"; + case Log.DEBUG: + return "DEBUG"; + case Log.INFO: + return "INFO"; + case Log.WARN: + return "WARN"; + case Log.ERROR: + return "ERROR"; + case Log.ASSERT: + return "ASSERT"; + } + + return "UNKNOWN"; + } + + + /** Default implementation logs to android.util.Log */ + public static class Print { + public int println(int priority, String msg ) { + return Log.println(priority,getScope(5), processMessage(msg)); + } + + protected String processMessage(String msg) { + if( config.minimumLogLevel <= Log.DEBUG ) + msg = String.format("%s %s", Thread.currentThread().getName(), msg); + return msg; + } + + protected static String getScope(int skipDepth) { + if( config.minimumLogLevel <= Log.DEBUG ) { + final StackTraceElement trace = Thread.currentThread().getStackTrace()[skipDepth]; + return config.scope + "/" + trace.getFileName() + ":" + trace.getLineNumber(); + } + + return config.scope; + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/util/SafeAsyncTask.java b/app/src/main/java/com/donnfelker/android/bootstrap/util/SafeAsyncTask.java new file mode 100644 index 0000000..e96f9f1 --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/util/SafeAsyncTask.java @@ -0,0 +1,296 @@ +package com.donnfelker.android.bootstrap.util; + +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import java.io.InterruptedIOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.*; + +/** + * Originally from RoboGuice: https://github.com/roboguice/roboguice/blob/master/roboguice/src/main/java/roboguice/util/SafeAsyncTask.java + * + * A class similar but unrelated to android's {@link android.os.AsyncTask}. + * + * Unlike AsyncTask, this class properly propagates exceptions. + * + * If you're familiar with AsyncTask and are looking for {@link android.os.AsyncTask#doInBackground(Object[])}, + * we've named it {@link #call()} here to conform with java 1.5's {@link java.util.concurrent.Callable} interface. + * + * Current limitations: does not yet handle progress, although it shouldn't be + * hard to add. + * + * If using your own executor, you must call future() to get a runnable you can execute. + * + * @param + */ +public abstract class SafeAsyncTask implements Callable { + public static final int DEFAULT_POOL_SIZE = 25; + protected static final Executor DEFAULT_EXECUTOR = Executors.newFixedThreadPool(DEFAULT_POOL_SIZE); + + protected Handler handler; + protected Executor executor; + protected StackTraceElement[] launchLocation; + protected FutureTask future; + + + /** + * Sets executor to Executors.newFixedThreadPool(DEFAULT_POOL_SIZE) and + * Handler to new Handler() + */ + public SafeAsyncTask() { + this.executor = DEFAULT_EXECUTOR; + } + + /** + * Sets executor to Executors.newFixedThreadPool(DEFAULT_POOL_SIZE) + */ + public SafeAsyncTask( Handler handler ) { + this.handler = handler; + this.executor = DEFAULT_EXECUTOR; + } + + /** + * Sets Handler to new Handler() + */ + public SafeAsyncTask( Executor executor ) { + this.executor = executor; + } + + public SafeAsyncTask( Handler handler, Executor executor ) { + this.handler = handler; + this.executor = executor; + } + + + public FutureTask future() { + future = new FutureTask( newTask() ); + return future; + } + + public SafeAsyncTask executor( Executor executor ) { + this.executor = executor; + return this; + } + + public Executor executor() { + return executor; + } + + public SafeAsyncTask handler( Handler handler ) { + this.handler = handler; + return this; + } + + public Handler handler() { + return handler; + } + + public void execute() { + execute(Thread.currentThread().getStackTrace()); + } + + protected void execute( StackTraceElement[] launchLocation ) { + this.launchLocation = launchLocation; + executor.execute( future() ); + } + + public boolean cancel( boolean mayInterruptIfRunning ) { + if( future==null ) + throw new UnsupportedOperationException("You cannot cancel this task before calling future()"); + + return future.cancel(mayInterruptIfRunning); + } + + + /** + * @throws Exception, captured on passed to onException() if present. + */ + protected void onPreExecute() throws Exception {} + + /** + * @param t the result of {@link #call()} + * @throws Exception, captured on passed to onException() if present. + */ + @SuppressWarnings({"UnusedDeclaration"}) + protected void onSuccess( ResultT t ) throws Exception {} + + /** + * Called when the thread has been interrupted, likely because + * the task was canceled. + * + * By default, calls {@link #onException(Exception)}, but this method + * may be overridden to handle interruptions differently than other + * exceptions. + * + * @param e an InterruptedException or InterruptedIOException + */ + protected void onInterrupted( Exception e ) { + onException(e); + } + + /** + * Logs the exception as an Error by default, but this method may + * be overridden by subclasses. + * + * @param e the exception thrown from {@link #onPreExecute()}, {@link #call()}, or {@link #onSuccess(Object)} + * @throws RuntimeException, ignored + */ + protected void onException( Exception e ) throws RuntimeException { + onThrowable(e); + } + + protected void onThrowable( Throwable t ) throws RuntimeException { + Log.e("roboguice", "Throwable caught during background processing", t); + } + + /** + * @throws RuntimeException, ignored + */ + protected void onFinally() throws RuntimeException {} + + + protected Task newTask() { + return new Task(this); + } + + + public static class Task implements Callable { + protected SafeAsyncTask parent; + protected Handler handler; + + public Task(SafeAsyncTask parent) { + this.parent = parent; + this.handler = parent.handler!=null ? parent.handler : new Handler(Looper.getMainLooper()); + } + + public Void call() throws Exception { + try { + doPreExecute(); + doSuccess(doCall()); + + } catch( final Exception e ) { + try { + doException(e); + } catch( Exception f ) { + // logged but ignored + Ln.e(f); + } + + } catch( final Throwable t ) { + try { + doThrowable(t); + } catch( Exception f ) { + // logged but ignored + Ln.e(f); + } + } finally { + doFinally(); + } + + return null; + } + + protected void doPreExecute() throws Exception { + postToUiThreadAndWait( new Callable() { + public Object call() throws Exception { + parent.onPreExecute(); + return null; + } + }); + } + + protected ResultT doCall() throws Exception { + return parent.call(); + } + + protected void doSuccess( final ResultT r ) throws Exception { + postToUiThreadAndWait( new Callable() { + public Object call() throws Exception { + parent.onSuccess(r); + return null; + } + }); + } + + protected void doException( final Exception e ) throws Exception { + if( parent.launchLocation!=null ) { + final ArrayList stack = new ArrayList(Arrays.asList(e.getStackTrace())); + stack.addAll(Arrays.asList(parent.launchLocation)); + e.setStackTrace(stack.toArray(new StackTraceElement[stack.size()])); + } + postToUiThreadAndWait( new Callable() { + public Object call() throws Exception { + if( e instanceof InterruptedException || e instanceof InterruptedIOException ) + parent.onInterrupted(e); + else + parent.onException(e); + return null; + } + }); + } + + protected void doThrowable( final Throwable e ) throws Exception { + if( parent.launchLocation!=null ) { + final ArrayList stack = new ArrayList(Arrays.asList(e.getStackTrace())); + stack.addAll(Arrays.asList(parent.launchLocation)); + e.setStackTrace(stack.toArray(new StackTraceElement[stack.size()])); + } + postToUiThreadAndWait( new Callable() { + public Object call() throws Exception { + parent.onThrowable(e); + return null; + } + }); + } + + protected void doFinally() throws Exception { + postToUiThreadAndWait( new Callable() { + public Object call() throws Exception { + parent.onFinally(); + return null; + } + }); + } + + + /** + * Posts the specified runnable to the UI thread using a handler, + * and waits for operation to finish. If there's an exception, + * it captures it and rethrows it. + * @param c the callable to post + * @throws Exception on error + */ + protected void postToUiThreadAndWait( final Callable c ) throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + final Exception[] exceptions = new Exception[1]; + + // Execute onSuccess in the UI thread, but wait + // for it to complete. + // If it throws an exception, capture that exception + // and rethrow it later. + handler.post( new Runnable() { + public void run() { + try { + c.call(); + } catch( Exception e ) { + exceptions[0] = e; + } finally { + latch.countDown(); + } + } + }); + + // Wait for onSuccess to finish + latch.await(); + + if( exceptions[0] != null ) + throw exceptions[0]; + + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/util/Strings.java b/app/src/main/java/com/donnfelker/android/bootstrap/util/Strings.java new file mode 100644 index 0000000..72a242b --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/util/Strings.java @@ -0,0 +1,181 @@ +package com.donnfelker.android.bootstrap.util; + + +import java.io.*; +import java.security.InvalidParameterException; +import java.security.MessageDigest; +import java.util.*; + +public class Strings { + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + + /** + * Originally from RoboGuice: https://github.com/roboguice/roboguice/blob/master/roboguice/src/main/java/roboguice/util/Strings.java + * Like join, but allows for a distinct final delimiter. For english sentences such + * as "Alice, Bob and Charlie" use ", " and " and " as the delimiters. + * + * @param delimiter usually ", " + * @param lastDelimiter usually " and " + * @param objs the objects + * @param the type + * @return a string + */ + public static String joinAnd( final String delimiter, final String lastDelimiter, final Collection objs ) { + if (objs == null || objs.isEmpty()) + return ""; + + final Iterator iter = objs.iterator(); + final StringBuilder buffer = new StringBuilder(Strings.toString(iter.next())); + int i=1; + while (iter.hasNext()) { + final T obj = iter.next(); + if(notEmpty(obj)) buffer.append( ++i == objs.size() ? lastDelimiter : delimiter).append(Strings.toString(obj)); + } + return buffer.toString(); + } + + public static String joinAnd( final String delimiter, final String lastDelimiter, final T... objs ) { + return joinAnd(delimiter, lastDelimiter, Arrays.asList(objs)); + } + + public static String join( final String delimiter, final Collection objs) { + if (objs == null || objs.isEmpty()) + return ""; + + final Iterator iter = objs.iterator(); + final StringBuilder buffer = new StringBuilder(Strings.toString(iter.next())); + + while (iter.hasNext()) { + final T obj = iter.next(); + if(notEmpty(obj)) buffer.append(delimiter).append(Strings.toString(obj)); + } + return buffer.toString(); + } + + public static String join(final String delimiter, final T... objects ) { + return join(delimiter, Arrays.asList(objects)); + } + + public static String toString(InputStream input) { + StringWriter sw = new StringWriter(); + copy( new InputStreamReader(input), sw); + return sw.toString(); + } + + public static String toString(Reader input) { + StringWriter sw = new StringWriter(); + copy(input, sw); + return sw.toString(); + } + + public static int copy(Reader input, Writer output) { + long count = copyLarge(input, output); + return count > Integer.MAX_VALUE ? -1 : (int)count; + } + + public static long copyLarge(Reader input, Writer output) throws RuntimeException { + try { + char[] buffer = new char[DEFAULT_BUFFER_SIZE]; + long count = 0; + int n; + while (-1 != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + return count; + } catch( IOException e ) { + throw new RuntimeException(e); + } + } + + public static String toString( final Object o ) { + return toString(o,""); + } + + public static String toString( final Object o, final String def ) { + return o==null ? def : + o instanceof InputStream ? toString((InputStream)o) : + o instanceof Reader ? toString((Reader)o) : + o instanceof Object[] ? Strings.join(", ",(Object[])o) : + o instanceof Collection ? Strings.join(", ", (Collection)o) : o.toString(); + } + + public static boolean isEmpty( final Object o ) { + return toString(o).trim().length()==0; + } + + public static boolean notEmpty( final Object o ) { + return toString(o).trim().length()!=0; + } + + public static String md5(String s) { + // http://stackoverflow.com/questions/1057041/difference-between-java-and-php5-md5-hash + // http://code.google.com/p/roboguice/issues/detail?id=89 + try { + + final byte[] hash = MessageDigest.getInstance( "MD5" ).digest(s.getBytes("UTF-8")); + final StringBuilder hashString = new StringBuilder(); + + for (byte aHash : hash) { + String hex = Integer.toHexString(aHash); + + if (hex.length() == 1) { + hashString.append('0'); + hashString.append(hex.charAt(hex.length() - 1)); + } else { + hashString.append(hex.substring(hex.length() - 2)); + } + } + + return hashString.toString(); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static String capitalize( String s ) { + final String c = Strings.toString(s); + return c.length()>=2 ? c.substring(0,1).toUpperCase() + c.substring(1) : c.length()>=1 ? c.toUpperCase() : c; + } + + public static boolean equals( Object a, Object b ) { + return Strings.toString(a).equals(Strings.toString(b)); + } + + public static boolean equalsIgnoreCase( Object a, Object b ) { + return Strings.toString(a).toLowerCase().equals(Strings.toString(b).toLowerCase()); + } + + public static String[] chunk( String str, int chunkSize ) { + if( isEmpty(str) || chunkSize==0 ) + return new String[0]; + + final int len = str.length(); + final int arrayLen = ((len-1)/chunkSize)+1; + final String[] array = new String[arrayLen]; + for( int i=0; i substitutions) { + for( String key : substitutions.keySet() ) + str = str.replace('$'+key,substitutions.get(key)); + + return str; + } + + public static String namedFormat( String str, Object... nameValuePairs ) { + if( nameValuePairs.length%2 != 0 ) + throw new InvalidParameterException("You must include one value for each parameter"); + + final HashMap map = new HashMap(nameValuePairs.length/2); + for( int i=0; i Date: Sat, 9 Mar 2013 15:42:23 -0700 Subject: [PATCH 28/68] Adding note about where authenticator activity is from --- .../authenticator/SherlockAccountAuthenticatorActivity.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/SherlockAccountAuthenticatorActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/SherlockAccountAuthenticatorActivity.java index 1dd168b..9e31db0 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/SherlockAccountAuthenticatorActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/SherlockAccountAuthenticatorActivity.java @@ -1,6 +1,9 @@ package com.donnfelker.android.bootstrap.authenticator; /* + * Originally from: + * https://github.com/rtyley/roboguice-sherlock/blob/master/src/main/java/com/github/rtyley/android/sherlock/android/accounts/SherlockAccountAuthenticatorActivity.java + * * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); From f4fd41e17eefb23791bb59822ca41512f3b3b720 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Mon, 11 Mar 2013 08:11:07 -0700 Subject: [PATCH 29/68] Adding some dagger module stuff. --- .../android/bootstrap/AndroidModule.java | 57 +++++++++++++++++++ .../android/bootstrap/BootstrapModule.java | 30 +++++++--- 2 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/donnfelker/android/bootstrap/AndroidModule.java diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/AndroidModule.java b/app/src/main/java/com/donnfelker/android/bootstrap/AndroidModule.java new file mode 100644 index 0000000..b22767a --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/AndroidModule.java @@ -0,0 +1,57 @@ +package com.donnfelker.android.bootstrap; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.preference.PreferenceManager; +import android.telephony.TelephonyManager; +import android.view.inputmethod.InputMethodManager; + +import dagger.Module; +import dagger.Provides; + +@Module( + complete = false +) +public class AndroidModule { + + @Provides + SharedPreferences provideDefaultSharedPreferences(final Context context) + { + return PreferenceManager.getDefaultSharedPreferences(context); + } + + @Provides + PackageInfo providePackageInfo(Context context) + { + try + { + return context.getPackageManager().getPackageInfo(context.getPackageName(), 0); + } + catch (PackageManager.NameNotFoundException e) + { + throw new RuntimeException(e); + } + } + + @Provides + TelephonyManager provideTelephonyManager(Context context) + { + return getSystemService(context, Context.TELEPHONY_SERVICE); + } + + @SuppressWarnings("unchecked") + public T getSystemService(Context context, String serviceConstant) + { + return (T) context.getSystemService(serviceConstant); + } + + @Provides + InputMethodManager provideInputMethodManager(final Context context) + { + return (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + } + + +} diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java index c36b28e..a8841c5 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java @@ -4,17 +4,33 @@ import com.google.inject.Singleton; import com.squareup.otto.Bus; +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; + /** - * Module for setting up custom bindings in RoboGuice. + * Dagger module for setting up provides statements. */ -public class BootstrapModule extends AbstractModule { +@Module +( + complete = false, + + entryPoints = { + BootstrapApplication.class + }, + + includes = { + AndroidModule.class + } +) +public class BootstrapModule { - @Override - protected void configure() { + @Singleton + @Provides + Bus provideOttoBus() { - // We want Otto to be bound as a singleton as one instance only needs - // to be present in this app - bind(Bus.class).in(Singleton.class); + return new Bus(); } From bbb86e0c5be047e9499ca6b625700351e19f4558 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Tue, 12 Mar 2013 06:52:24 -0700 Subject: [PATCH 30/68] Updating code to include dagger ioc. Still does not build, broken on generic implementation. Needs work. --- .../android/bootstrap/AndroidModule.java | 31 +++++++++++++++- .../bootstrap/BootstrapApplication.java | 8 +++++ .../android/bootstrap/BootstrapModule.java | 36 +++++++++++++++---- .../bootstrap/BootstrapServiceProvider.java | 3 +- .../android/bootstrap/RootModule.java | 16 +++++++++ .../authenticator/ApiKeyProvider.java | 3 +- .../authenticator/LogoutService.java | 18 +++++----- .../bootstrap/core/UserAgentProvider.java | 11 +++--- .../bootstrap/ui/CheckInsListFragment.java | 15 +++++++- .../bootstrap/ui/ItemListFragment.java | 14 +++----- .../bootstrap/ui/NewsListFragment.java | 15 +++++++- .../bootstrap/ui/UserListFragment.java | 12 ++++++- 12 files changed, 145 insertions(+), 37 deletions(-) create mode 100644 app/src/main/java/com/donnfelker/android/bootstrap/RootModule.java diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/AndroidModule.java b/app/src/main/java/com/donnfelker/android/bootstrap/AndroidModule.java index b22767a..0b06cb5 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/AndroidModule.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/AndroidModule.java @@ -1,21 +1,36 @@ package com.donnfelker.android.bootstrap; +import android.accounts.AccountManager; import android.content.Context; import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.preference.PreferenceManager; import android.telephony.TelephonyManager; import android.view.inputmethod.InputMethodManager; +import javax.inject.Singleton; + import dagger.Module; import dagger.Provides; -@Module( +/** + * Module for all Android related provisions + */ +@Module +( complete = false ) public class AndroidModule { + @Provides + @Singleton + Context provideAppContext() + { + return BootstrapApplication.getInstance().getApplicationContext(); + } + @Provides SharedPreferences provideDefaultSharedPreferences(final Context context) { @@ -53,5 +68,19 @@ InputMethodManager provideInputMethodManager(final Context context) return (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); } + @Provides + ApplicationInfo provideApplicationInfo(final Context context) { + return context.getApplicationInfo(); + } + + @Provides + AccountManager provideAccountManager(final Context context) { + return AccountManager.get(context); + } + + @Provides + ClassLoader provideClassLoader(final Context context) { + return context.getClassLoader(); + } } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java index 11f3295..7430931 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java @@ -15,10 +15,15 @@ */ public class BootstrapApplication extends Application { + private static BootstrapApplication instance; + /** * Create main application */ public BootstrapApplication() { + + instance = this; + // Disable http.keepAlive on Froyo and below if (SDK_INT <= FROYO) HttpRequest.keepAlive(false); @@ -47,4 +52,7 @@ public BootstrapApplication(final Instrumentation instrumentation) { } + public static BootstrapApplication getInstance() { + return instance; + } } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java index a8841c5..cf0e707 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java @@ -1,7 +1,18 @@ package com.donnfelker.android.bootstrap; -import com.google.inject.AbstractModule; -import com.google.inject.Singleton; +import android.accounts.AccountManager; +import android.content.Context; + +import com.donnfelker.android.bootstrap.authenticator.LogoutService; +import com.donnfelker.android.bootstrap.core.CheckIn; +import com.donnfelker.android.bootstrap.ui.BootstrapTimerActivity; +import com.donnfelker.android.bootstrap.ui.CarouselActivity; +import com.donnfelker.android.bootstrap.ui.CheckInsListFragment; +import com.donnfelker.android.bootstrap.ui.ItemListFragment; +import com.donnfelker.android.bootstrap.ui.NewsActivity; +import com.donnfelker.android.bootstrap.ui.NewsListFragment; +import com.donnfelker.android.bootstrap.ui.UserActivity; +import com.donnfelker.android.bootstrap.ui.UserListFragment; import com.squareup.otto.Bus; import javax.inject.Singleton; @@ -11,18 +22,23 @@ /** * Dagger module for setting up provides statements. + * Register all of your entry points below. */ @Module ( complete = false, entryPoints = { - BootstrapApplication.class - }, - - includes = { - AndroidModule.class + BootstrapApplication.class, + CarouselActivity.class, + BootstrapTimerActivity.class, + CheckInsListFragment.class, + NewsActivity.class, + NewsListFragment.class, + UserActivity.class, + UserListFragment.class } + ) public class BootstrapModule { @@ -34,4 +50,10 @@ Bus provideOttoBus() { } + @Provides + @Singleton + LogoutService provideLogoutService(final Context context, final AccountManager accountManager) { + return new LogoutService(context, accountManager); + } + } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapServiceProvider.java b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapServiceProvider.java index 779d945..5ca840c 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapServiceProvider.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapServiceProvider.java @@ -2,6 +2,7 @@ package com.donnfelker.android.bootstrap; import android.accounts.AccountsException; +import android.app.Activity; import com.donnfelker.android.bootstrap.authenticator.ApiKeyProvider; import com.donnfelker.android.bootstrap.core.BootstrapService; @@ -27,7 +28,7 @@ public class BootstrapServiceProvider { * @throws IOException * @throws AccountsException */ - public BootstrapService getService() throws IOException, AccountsException { + public BootstrapService getService(Activity activity) throws IOException, AccountsException { return new BootstrapService(keyProvider.getAuthKey(), userAgentProvider); } } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/RootModule.java b/app/src/main/java/com/donnfelker/android/bootstrap/RootModule.java new file mode 100644 index 0000000..dbc06b6 --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/RootModule.java @@ -0,0 +1,16 @@ +package com.donnfelker.android.bootstrap; + +import dagger.Module; + +/** + * Add all the other modules to this one. + */ +@Module +( + includes = { + AndroidModule.class, + BootstrapModule.class + } +) +public class RootModule { +} diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/ApiKeyProvider.java b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/ApiKeyProvider.java index 1b9f1a0..f4e1f76 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/ApiKeyProvider.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/ApiKeyProvider.java @@ -20,7 +20,6 @@ */ public class ApiKeyProvider { - @Inject private Activity activity; @Inject private AccountManager accountManager; /** @@ -30,7 +29,7 @@ public class ApiKeyProvider { * @throws AccountsException * @throws IOException */ - public String getAuthKey() throws AccountsException, IOException { + public String getAuthKey(Activity activity) throws AccountsException, IOException { AccountManagerFuture accountManagerFuture = accountManager.getAuthTokenByFeatures(Constants.Auth.BOOTSTRAP_ACCOUNT_TYPE, Constants.Auth.AUTHTOKEN_TYPE, new String[0], activity, null, null, null, null); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/LogoutService.java b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/LogoutService.java index 1e0003b..4a2d74c 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/LogoutService.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/LogoutService.java @@ -11,18 +11,20 @@ import javax.inject.Inject; -import java.util.concurrent.Callable; -import java.util.concurrent.Executor; - -// TODO-dagger: Mark this a s singleton of some sort, maybe. -// @ContextSingleton +/** + * Class used for logging a user out. + */ public class LogoutService { - @Inject protected Context context; - @Inject protected AccountManager accountManager; - + protected Context context; + protected AccountManager accountManager; + @Inject + public LogoutService(Context context, AccountManager accountManager) { + this.context = context; + this.accountManager = accountManager; + } public void logout(final Runnable onSuccess) { diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/UserAgentProvider.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/UserAgentProvider.java index 3dabda0..bf82e88 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/core/UserAgentProvider.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/UserAgentProvider.java @@ -1,27 +1,26 @@ package com.donnfelker.android.bootstrap.core; -import android.app.Application; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.os.Build; import android.telephony.TelephonyManager; +import com.donnfelker.android.bootstrap.BootstrapApplication; import com.donnfelker.android.bootstrap.util.Ln; import com.donnfelker.android.bootstrap.util.Strings; import javax.inject.Inject; import javax.inject.Provider; - - import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Locale; public class UserAgentProvider implements Provider { - @Inject protected Application app; + @Inject protected ApplicationInfo appInfo; @Inject protected PackageInfo info; @Inject protected TelephonyManager telephonyManager; + @Inject protected ClassLoader classLoader; protected String userAgent; @@ -44,13 +43,13 @@ public String get() { ); final ArrayList params = new ArrayList(); - params.add( "preload=" + ((app.getApplicationInfo().flags& ApplicationInfo.FLAG_SYSTEM)==1) ); // Determine if this app was a preloaded app + params.add( "preload=" + ((appInfo.flags& ApplicationInfo.FLAG_SYSTEM)==1) ); // Determine if this app was a preloaded app params.add( "locale=" + Locale.getDefault() ); // http://stackoverflow.com/questions/2641111/where-is-android-os-systemproperties try{ - final Class SystemProperties = app.getClassLoader().loadClass("android.os.SystemProperties"); + final Class SystemProperties = classLoader.loadClass("android.os.SystemProperties"); final Method get = SystemProperties.getMethod("get", String.class); params.add( "clientidbase=" + get.invoke(SystemProperties, "ro.com.google.clientidbase")); }catch( Exception ignored ){ diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java index 0d676bf..b896af9 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java @@ -11,16 +11,19 @@ import com.donnfelker.android.bootstrap.BootstrapServiceProvider; import com.donnfelker.android.bootstrap.R; +import com.donnfelker.android.bootstrap.authenticator.LogoutService; import com.donnfelker.android.bootstrap.core.CheckIn; import com.github.kevinsawicki.wishlist.SingleTypeAdapter; import javax.inject.Inject; import javax.inject.Inject; +import java.util.Collections; import java.util.List; public class CheckInsListFragment extends ItemListFragment { @Inject protected BootstrapServiceProvider serviceProvider; + @Inject protected LogoutService logoutService; @Override protected void configureList(Activity activity, ListView listView) { @@ -34,6 +37,11 @@ protected void configureList(Activity activity, ListView listView) { .inflate(R.layout.checkins_list_item_labels, null)); } + @Override + LogoutService getLogoutService() { + return logoutService; + } + @Override public void onDestroyView() { setListAdapter(null); @@ -49,7 +57,12 @@ public Loader> onCreateLoader(int id, Bundle args) { @Override public List loadData() throws Exception { try { - return serviceProvider.getService().getCheckIns(); + if(getActivity() != null) { + return serviceProvider.getService(getActivity()).getCheckIns(); + } else { + return Collections.emptyList(); + } + } catch (OperationCanceledException e) { Activity activity = getActivity(); if (activity != null) diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/ItemListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/ItemListFragment.java index d7fc22c..74c359b 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/ItemListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/ItemListFragment.java @@ -1,7 +1,6 @@ package com.donnfelker.android.bootstrap.ui; -import android.accounts.AccountManager; import android.app.Activity; import android.os.Bundle; import android.support.v4.app.LoaderManager.LoaderCallbacks; @@ -22,18 +21,15 @@ import com.actionbarsherlock.view.MenuInflater; import com.actionbarsherlock.view.MenuItem; import com.donnfelker.android.bootstrap.R; +import com.donnfelker.android.bootstrap.R.id; +import com.donnfelker.android.bootstrap.R.layout; import com.donnfelker.android.bootstrap.authenticator.LogoutService; import com.github.kevinsawicki.wishlist.SingleTypeAdapter; import com.github.kevinsawicki.wishlist.Toaster; import com.github.kevinsawicki.wishlist.ViewUtils; -import com.donnfelker.android.bootstrap.R.id; -import com.donnfelker.android.bootstrap.R.layout; -import com.donnfelker.android.bootstrap.R.menu; -import javax.inject.Inject; import java.util.Collections; import java.util.List; -import java.util.concurrent.Callable; @@ -46,8 +42,6 @@ public abstract class ItemListFragment extends SherlockFragment implements LoaderCallbacks> { - @Inject protected LogoutService logoutService; - private static final String FORCE_REFRESH = "forceRefresh"; /** @@ -172,8 +166,10 @@ public boolean onOptionsItemSelected(MenuItem item) { } } + abstract LogoutService getLogoutService(); + private void logout() { - logoutService.logout(new Runnable() { + getLogoutService().logout(new Runnable() { @Override public void run() { // Calling a refresh will force the service to look for a logged in user diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java index 34cf93a..3920b23 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java @@ -11,15 +11,18 @@ import com.donnfelker.android.bootstrap.BootstrapServiceProvider; import com.donnfelker.android.bootstrap.R; +import com.donnfelker.android.bootstrap.authenticator.LogoutService; import com.donnfelker.android.bootstrap.core.News; import com.github.kevinsawicki.wishlist.SingleTypeAdapter; import javax.inject.Inject; +import java.util.Collections; import java.util.List; public class NewsListFragment extends ItemListFragment { @Inject protected BootstrapServiceProvider serviceProvider; + @Inject protected LogoutService logoutService; @Override public void onActivityCreated(Bundle savedInstanceState) { @@ -40,6 +43,11 @@ protected void configureList(Activity activity, ListView listView) { .inflate(R.layout.news_list_item_labels, null)); } + @Override + LogoutService getLogoutService() { + return logoutService; + } + @Override public void onDestroyView() { setListAdapter(null); @@ -55,7 +63,12 @@ public Loader> onCreateLoader(int id, Bundle args) { @Override public List loadData() throws Exception { try { - return serviceProvider.getService().getNews(); + if(getActivity() != null) { + return serviceProvider.getService(getActivity()).getNews(); + } else { + return Collections.emptyList(); + } + } catch (OperationCanceledException e) { Activity activity = getActivity(); if (activity != null) diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java index c527efe..3b42235 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java @@ -12,6 +12,7 @@ import com.donnfelker.android.bootstrap.BootstrapServiceProvider; import com.donnfelker.android.bootstrap.R; +import com.donnfelker.android.bootstrap.authenticator.LogoutService; import com.donnfelker.android.bootstrap.core.AvatarLoader; import com.donnfelker.android.bootstrap.core.News; import com.donnfelker.android.bootstrap.core.User; @@ -25,6 +26,7 @@ public class UserListFragment extends ItemListFragment { @Inject private BootstrapServiceProvider serviceProvider; @Inject private AvatarLoader avatars; + @Inject protected LogoutService logoutService; @Override public void onActivityCreated(Bundle savedInstanceState) { @@ -44,6 +46,10 @@ protected void configureList(Activity activity, ListView listView) { .inflate(R.layout.user_list_item_labels, null)); } + @Override + LogoutService getLogoutService() { + return logoutService; + } @Override @@ -54,7 +60,11 @@ public Loader> onCreateLoader(int id, Bundle args) { public List loadData() throws Exception { try { - List latest = serviceProvider.getService().getUsers(); + List latest = null; + + if(getActivity() != null) + latest = serviceProvider.getService(getActivity()).getUsers(); + if (latest != null) return latest; else From 1ee9448fb0eeff7f8ca5e76dcb0026f24272f7cf Mon Sep 17 00:00:00 2001 From: Jake Wharton Date: Thu, 14 Mar 2013 09:26:07 -0700 Subject: [PATCH 31/68] Bump to latest Butter Knife and Dagger (SNAPSHOT). Fix a few field scopes to enable proper code generation for both libraries. --- app/pom.xml | 10 +++++----- .../bootstrap/BootstrapServiceProvider.java | 4 ++-- .../bootstrap/authenticator/ApiKeyProvider.java | 2 +- .../BootstrapAuthenticatorActivity.java | 6 +++--- .../android/bootstrap/core/TimerService.java | 2 +- .../android/bootstrap/ui/CarouselActivity.java | 4 ++-- .../android/bootstrap/ui/UserListFragment.java | 4 ++-- pom.xml | 14 ++++++++++++++ 8 files changed, 30 insertions(+), 16 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 5696d20..c62b8b1 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -17,7 +17,7 @@ 4.2.0 - 0.9 + 1.0-SNAPSHOT @@ -32,12 +32,12 @@ 2.2.2 - com.squareup + com.squareup.dagger dagger ${dagger.version} - com.squareup + com.squareup.dagger dagger-compiler ${dagger.version} true @@ -45,7 +45,7 @@ com.jakewharton butterknife - 1.2.0 + 1.2.2 com.actionbarsherlock @@ -118,7 +118,7 @@ android-maven-plugin - com.squareup + com.squareup.dagger dagger-compiler ${dagger.version} diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapServiceProvider.java b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapServiceProvider.java index 5ca840c..6d03c8e 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapServiceProvider.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapServiceProvider.java @@ -16,8 +16,8 @@ */ public class BootstrapServiceProvider { - @Inject private ApiKeyProvider keyProvider; - @Inject private UserAgentProvider userAgentProvider; + @Inject ApiKeyProvider keyProvider; + @Inject UserAgentProvider userAgentProvider; /** * Get service for configured key provider diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/ApiKeyProvider.java b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/ApiKeyProvider.java index f4e1f76..d196578 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/ApiKeyProvider.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/ApiKeyProvider.java @@ -20,7 +20,7 @@ */ public class ApiKeyProvider { - @Inject private AccountManager accountManager; + @Inject AccountManager accountManager; /** * This call blocks, so shouldn't be called on the UI thread diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAuthenticatorActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAuthenticatorActivity.java index 93eb4a4..6f55bd9 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAuthenticatorActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAuthenticatorActivity.java @@ -88,9 +88,9 @@ public class BootstrapAuthenticatorActivity extends SherlockAccountAuthenticator private AccountManager accountManager; - @InjectView(id.et_email) private AutoCompleteTextView emailText; - @InjectView(id.et_password) private EditText passwordText; - @InjectView(id.b_signin) private Button signinButton; + @InjectView(id.et_email) AutoCompleteTextView emailText; + @InjectView(id.et_password) EditText passwordText; + @InjectView(id.b_signin) Button signinButton; private TextWatcher watcher = validationTextWatcher(); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java index 2bb633a..1f08480 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java @@ -25,7 +25,7 @@ public class TimerService extends Service { @Inject protected Bus BUS; - @Inject private NotificationManager notificationManager; + @Inject NotificationManager notificationManager; private boolean timerRunning = false; private boolean timerStarted; diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java index c89f0d0..5e6277d 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java @@ -23,8 +23,8 @@ */ public class CarouselActivity extends BootstrapFragmentActivity { - @InjectView(id.tpi_header) private TitlePageIndicator indicator; - @InjectView(id.vp_pages) private ViewPager pager; + @InjectView(id.tpi_header) TitlePageIndicator indicator; + @InjectView(id.vp_pages) ViewPager pager; @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java index 3b42235..331ceec 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java @@ -24,8 +24,8 @@ public class UserListFragment extends ItemListFragment { - @Inject private BootstrapServiceProvider serviceProvider; - @Inject private AvatarLoader avatars; + @Inject BootstrapServiceProvider serviceProvider; + @Inject AvatarLoader avatars; @Inject protected LogoutService logoutService; @Override diff --git a/pom.xml b/pom.xml index ce4b276..0c48910 100644 --- a/pom.xml +++ b/pom.xml @@ -69,4 +69,18 @@ + + + + sonatype-nexus-snapshots + Sonatype Nexus Snapshots + https://oss.sonatype.org/content/repositories/snapshots + + false + + + true + + + From 38714c5c98f90d5b43874c074df8708c3535b89a Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Thu, 14 Mar 2013 15:56:46 -0700 Subject: [PATCH 32/68] Fixing bug with activity --- .../donnfelker/android/bootstrap/BootstrapServiceProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapServiceProvider.java b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapServiceProvider.java index 6d03c8e..75797ff 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapServiceProvider.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapServiceProvider.java @@ -29,6 +29,6 @@ public class BootstrapServiceProvider { * @throws AccountsException */ public BootstrapService getService(Activity activity) throws IOException, AccountsException { - return new BootstrapService(keyProvider.getAuthKey(), userAgentProvider); + return new BootstrapService(keyProvider.getAuthKey(activity), userAgentProvider); } } From 883a333b221d33c2cf429b2a1250f100db87a180 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Thu, 14 Mar 2013 20:25:03 -0700 Subject: [PATCH 33/68] Attempting to perform injections on bootstrap entry points --- .../bootstrap/BootstrapApplication.java | 27 +++++++++++++++++-- .../android/bootstrap/BootstrapModule.java | 4 +-- .../android/bootstrap/core/TimerService.java | 3 +++ .../ui/BootstrapFragmentActivity.java | 5 +++- .../bootstrap/ui/BootstrapTimerActivity.java | 5 +++- .../bootstrap/ui/CarouselActivity.java | 3 +++ .../bootstrap/ui/CheckInsListFragment.java | 8 ++++++ .../bootstrap/ui/NewsListFragment.java | 3 +++ .../bootstrap/ui/UserListFragment.java | 5 +++- 9 files changed, 56 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java index 7430931..e33a85f 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java @@ -10,20 +10,21 @@ import com.github.kevinsawicki.http.HttpRequest; +import dagger.ObjectGraph; + /** * Android Bootstrap application */ public class BootstrapApplication extends Application { private static BootstrapApplication instance; + ObjectGraph objectGraph; /** * Create main application */ public BootstrapApplication() { - instance = this; - // Disable http.keepAlive on Froyo and below if (SDK_INT <= FROYO) HttpRequest.keepAlive(false); @@ -37,8 +38,24 @@ public BootstrapApplication() { public BootstrapApplication(final Context context) { this(); attachBaseContext(context); + } + @Override + public void onCreate() { + super.onCreate(); + + instance = this; + // Perform Injection + objectGraph = ObjectGraph.create(getRootModule()); + objectGraph.inject(this); + objectGraph.injectStatics(); + + } + + private Object getRootModule() { + return new RootModule(); + } /** @@ -51,6 +68,12 @@ public BootstrapApplication(final Instrumentation instrumentation) { attachBaseContext(instrumentation.getTargetContext()); } + public void inject(Object object) + { + objectGraph.inject(object); + } + + public static BootstrapApplication getInstance() { return instance; diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java index cf0e707..9051f70 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java @@ -3,6 +3,7 @@ import android.accounts.AccountManager; import android.content.Context; +import com.donnfelker.android.bootstrap.authenticator.BootstrapAuthenticatorActivity; import com.donnfelker.android.bootstrap.authenticator.LogoutService; import com.donnfelker.android.bootstrap.core.CheckIn; import com.donnfelker.android.bootstrap.ui.BootstrapTimerActivity; @@ -30,6 +31,7 @@ entryPoints = { BootstrapApplication.class, + BootstrapAuthenticatorActivity.class, CarouselActivity.class, BootstrapTimerActivity.class, CheckInsListFragment.class, @@ -45,9 +47,7 @@ public class BootstrapModule { @Singleton @Provides Bus provideOttoBus() { - return new Bus(); - } @Provides diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java index 1f08480..c210a12 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java @@ -11,6 +11,7 @@ import android.os.SystemClock; import android.support.v4.app.NotificationCompat; +import com.donnfelker.android.bootstrap.BootstrapApplication; import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.ui.BootstrapTimerActivity; import javax.inject.Inject; @@ -45,6 +46,8 @@ public IBinder onBind(Intent intent) { public void onCreate() { super.onCreate(); + BootstrapApplication.getInstance().inject(this); + // Register the bus so we can send notifications. BUS.register(this); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java index 8082ae1..6ee34f4 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java @@ -1,6 +1,7 @@ package com.donnfelker.android.bootstrap.ui; import com.actionbarsherlock.app.SherlockFragmentActivity; +import com.donnfelker.android.bootstrap.BootstrapApplication; import butterknife.Views; @@ -14,6 +15,8 @@ public void setContentView(int layoutResId) { super.setContentView(layoutResId); // Perform view injection via butter knife - Views.inject(this); + // Doesnt seem like you can inject via a super class. Throws method not found. + //Views.inject(this); + BootstrapApplication.getInstance().inject(this); } } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java index 7eaeadf..26c666e 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java @@ -20,10 +20,11 @@ import com.squareup.otto.Subscribe; import butterknife.InjectView; +import butterknife.Views; public class BootstrapTimerActivity extends BootstrapFragmentActivity implements View.OnClickListener { - @Inject protected Bus BUS; + @Inject Bus BUS; @InjectView(R.id.chronometer) protected TextView chronometer; @InjectView(R.id.start) protected Button start; @@ -37,6 +38,8 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.bootstrap_timer); + Views.inject(this); + setTitle(R.string.timer); start.setOnClickListener(this); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java index 5e6277d..4151a9d 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java @@ -17,6 +17,7 @@ import com.viewpagerindicator.TitlePageIndicator; import butterknife.InjectView; +import butterknife.Views; /** * Activity to view the carousel and view pager indicator with fragments. @@ -34,6 +35,8 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.carousel_view); + Views.inject(this); + pager.setAdapter(new BootstrapPagerAdapter(getResources(), getSupportFragmentManager())); indicator.setViewPager(pager); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java index b896af9..fc9d786 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java @@ -9,6 +9,7 @@ import android.view.View; import android.widget.ListView; +import com.donnfelker.android.bootstrap.BootstrapApplication; import com.donnfelker.android.bootstrap.BootstrapServiceProvider; import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.authenticator.LogoutService; @@ -25,6 +26,13 @@ public class CheckInsListFragment extends ItemListFragment { @Inject protected BootstrapServiceProvider serviceProvider; @Inject protected LogoutService logoutService; + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + BootstrapApplication.getInstance().inject(this); + } + @Override protected void configureList(Activity activity, ListView listView) { super.configureList(activity, listView); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java index 3920b23..455aeea 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java @@ -9,6 +9,7 @@ import android.view.View; import android.widget.ListView; +import com.donnfelker.android.bootstrap.BootstrapApplication; import com.donnfelker.android.bootstrap.BootstrapServiceProvider; import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.authenticator.LogoutService; @@ -29,6 +30,8 @@ public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setEmptyText(R.string.no_news); + + BootstrapApplication.getInstance().inject(this); } @Override diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java index 331ceec..d804e6e 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java @@ -10,6 +10,7 @@ import android.view.View; import android.widget.ListView; +import com.donnfelker.android.bootstrap.BootstrapApplication; import com.donnfelker.android.bootstrap.BootstrapServiceProvider; import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.authenticator.LogoutService; @@ -26,13 +27,15 @@ public class UserListFragment extends ItemListFragment { @Inject BootstrapServiceProvider serviceProvider; @Inject AvatarLoader avatars; - @Inject protected LogoutService logoutService; + @Inject LogoutService logoutService; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setEmptyText(R.string.no_users); + + BootstrapApplication.getInstance().inject(this); } @Override From b37181c5a1a4ae18793dbf98383a620a1c84f2e8 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Thu, 14 Mar 2013 20:36:28 -0700 Subject: [PATCH 34/68] Adding injection to activities, for some reason the injection doesnt work on base classes --- .../authenticator/BootstrapAuthenticatorActivity.java | 3 +++ .../com/donnfelker/android/bootstrap/ui/CarouselActivity.java | 2 ++ 2 files changed, 5 insertions(+) diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAuthenticatorActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAuthenticatorActivity.java index 6f55bd9..b88f934 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAuthenticatorActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAuthenticatorActivity.java @@ -56,6 +56,7 @@ import java.util.List; import butterknife.InjectView; +import butterknife.Views; import static com.donnfelker.android.bootstrap.core.Constants.Http.USERNAME; import static com.donnfelker.android.bootstrap.core.Constants.Http.PASSWORD; @@ -135,6 +136,8 @@ public void onCreate(Bundle bundle) { setContentView(layout.login_activity); + Views.inject(this); + emailText.setAdapter(new ArrayAdapter(this, simple_dropdown_item_1line, userEmailAccounts())); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java index 4151a9d..6707f7a 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java @@ -11,6 +11,7 @@ import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.Window; +import com.donnfelker.android.bootstrap.BootstrapApplication; import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.R.id; @@ -36,6 +37,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.carousel_view); Views.inject(this); + BootstrapApplication.getInstance().inject(this); pager.setAdapter(new BootstrapPagerAdapter(getResources(), getSupportFragmentManager())); From 293e8812d490515494707b618a2bf7aebe52a1ea Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Thu, 14 Mar 2013 21:00:01 -0700 Subject: [PATCH 35/68] Adding injection at the correct locations --- .../com/donnfelker/android/bootstrap/AndroidModule.java | 6 ++++++ .../donnfelker/android/bootstrap/BootstrapModule.java | 4 +++- .../android/bootstrap/ui/BootstrapFragmentActivity.java | 9 +-------- .../android/bootstrap/ui/BootstrapTimerActivity.java | 3 +++ .../android/bootstrap/ui/CheckInsListFragment.java | 8 +++++++- .../android/bootstrap/ui/NewsListFragment.java | 9 ++++++++- .../donnfelker/android/bootstrap/ui/UserActivity.java | 4 ++++ .../android/bootstrap/ui/UserListFragment.java | 9 ++++++++- 8 files changed, 40 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/AndroidModule.java b/app/src/main/java/com/donnfelker/android/bootstrap/AndroidModule.java index 0b06cb5..903e8cd 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/AndroidModule.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/AndroidModule.java @@ -1,6 +1,7 @@ package com.donnfelker.android.bootstrap; import android.accounts.AccountManager; +import android.app.NotificationManager; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; @@ -83,4 +84,9 @@ ClassLoader provideClassLoader(final Context context) { return context.getClassLoader(); } + @Provides + NotificationManager provideNotificationManager(final Context context) { + return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + } + } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java index 9051f70..8e91be0 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java @@ -6,6 +6,7 @@ import com.donnfelker.android.bootstrap.authenticator.BootstrapAuthenticatorActivity; import com.donnfelker.android.bootstrap.authenticator.LogoutService; import com.donnfelker.android.bootstrap.core.CheckIn; +import com.donnfelker.android.bootstrap.core.TimerService; import com.donnfelker.android.bootstrap.ui.BootstrapTimerActivity; import com.donnfelker.android.bootstrap.ui.CarouselActivity; import com.donnfelker.android.bootstrap.ui.CheckInsListFragment; @@ -38,7 +39,8 @@ NewsActivity.class, NewsListFragment.class, UserActivity.class, - UserListFragment.class + UserListFragment.class, + TimerService.class } ) diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java index 6ee34f4..f875eec 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java @@ -10,13 +10,6 @@ */ public class BootstrapFragmentActivity extends SherlockFragmentActivity { - @Override - public void setContentView(int layoutResId) { - super.setContentView(layoutResId); + // Handle any super class related items here. - // Perform view injection via butter knife - // Doesnt seem like you can inject via a super class. Throws method not found. - //Views.inject(this); - BootstrapApplication.getInstance().inject(this); - } } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java index 26c666e..16c4340 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java @@ -8,6 +8,7 @@ import android.widget.Button; import android.widget.TextView; +import com.donnfelker.android.bootstrap.BootstrapApplication; import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.core.PauseTimerEvent; import com.donnfelker.android.bootstrap.core.ResumeTimerEvent; @@ -38,6 +39,8 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.bootstrap_timer); + BootstrapApplication.getInstance().inject(this); + Views.inject(this); setTitle(R.string.timer); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java index fc9d786..25f98f1 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java @@ -26,11 +26,17 @@ public class CheckInsListFragment extends ItemListFragment { @Inject protected BootstrapServiceProvider serviceProvider; @Inject protected LogoutService logoutService; + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + BootstrapApplication.getInstance().inject(this); + } + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - BootstrapApplication.getInstance().inject(this); + } @Override diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java index 455aeea..246e94a 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java @@ -25,13 +25,20 @@ public class NewsListFragment extends ItemListFragment { @Inject protected BootstrapServiceProvider serviceProvider; @Inject protected LogoutService logoutService; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + BootstrapApplication.getInstance().inject(this); + } + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setEmptyText(R.string.no_news); - BootstrapApplication.getInstance().inject(this); + } @Override diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserActivity.java index 44caaa1..a150c9b 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserActivity.java @@ -5,12 +5,14 @@ import android.widget.ImageView; import android.widget.TextView; +import com.donnfelker.android.bootstrap.BootstrapApplication; import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.core.AvatarLoader; import com.donnfelker.android.bootstrap.core.User; import javax.inject.Inject; import butterknife.InjectView; +import butterknife.Views; public class UserActivity extends BootstrapActivity { @@ -27,6 +29,8 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.user_view); + BootstrapApplication.getInstance().inject(this); + if(getIntent() != null && getIntent().getExtras() != null) { user = (User) getIntent().getExtras().getSerializable(USER); } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java index d804e6e..74ec7cc 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java @@ -29,13 +29,20 @@ public class UserListFragment extends ItemListFragment { @Inject AvatarLoader avatars; @Inject LogoutService logoutService; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + BootstrapApplication.getInstance().inject(this); + } + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setEmptyText(R.string.no_users); - BootstrapApplication.getInstance().inject(this); + } @Override From 1c6c032f00b918eb47e549d4eb228a9829a41100 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Fri, 15 Mar 2013 08:38:10 -0700 Subject: [PATCH 36/68] Moving injection to super classes --- .../android/bootstrap/ui/BootstrapActivity.java | 4 ++++ .../bootstrap/ui/BootstrapFragmentActivity.java | 15 ++++++++++++++- .../bootstrap/ui/BootstrapTimerActivity.java | 4 ---- .../android/bootstrap/ui/CarouselActivity.java | 3 --- .../android/bootstrap/ui/UserActivity.java | 2 -- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapActivity.java index 1771e14..7a1651c 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapActivity.java @@ -6,6 +6,7 @@ import com.actionbarsherlock.app.SherlockActivity; import com.actionbarsherlock.view.MenuItem; +import com.donnfelker.android.bootstrap.BootstrapApplication; import butterknife.Views; @@ -18,6 +19,9 @@ public abstract class BootstrapActivity extends SherlockActivity { public void setContentView(int layoutResId) { super.setContentView(layoutResId); + + BootstrapApplication.getInstance().inject(this); + // Used to inject views with the Butterknife library Views.inject(this); } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java index f875eec..dc23136 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java @@ -1,5 +1,7 @@ package com.donnfelker.android.bootstrap.ui; +import android.os.Bundle; + import com.actionbarsherlock.app.SherlockFragmentActivity; import com.donnfelker.android.bootstrap.BootstrapApplication; @@ -9,7 +11,18 @@ * Base class for all Bootstrap Activities that need fragments. */ public class BootstrapFragmentActivity extends SherlockFragmentActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + BootstrapApplication.getInstance().inject(this); + } + + @Override + public void setContentView(int layoutResId) { + super.setContentView(layoutResId); - // Handle any super class related items here. + Views.inject(this); + } } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java index 16c4340..567c7b5 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapTimerActivity.java @@ -39,10 +39,6 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.bootstrap_timer); - BootstrapApplication.getInstance().inject(this); - - Views.inject(this); - setTitle(R.string.timer); start.setOnClickListener(this); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java index 6707f7a..6cf6021 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java @@ -36,9 +36,6 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.carousel_view); - Views.inject(this); - BootstrapApplication.getInstance().inject(this); - pager.setAdapter(new BootstrapPagerAdapter(getResources(), getSupportFragmentManager())); indicator.setViewPager(pager); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserActivity.java index a150c9b..6a3f964 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserActivity.java @@ -29,8 +29,6 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.user_view); - BootstrapApplication.getInstance().inject(this); - if(getIntent() != null && getIntent().getExtras() != null) { user = (User) getIntent().getExtras().getSerializable(USER); } From 1229d01365dfe1c62bc57d632a9303d16ca8eb71 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Fri, 15 Mar 2013 08:44:16 -0700 Subject: [PATCH 37/68] Updating readme --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b37175e..eb5cf27 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,10 @@ The application * [Apache Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html) -Copyright 2012 Donn Felker +Copyright 2013 Donn Felker -Copyright 2012 GitHub Inc. +Copyright 2013 GitHub Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -107,7 +107,9 @@ and uses many great open-source libraries from the Android dev community: for swiping between fragments and [NineOldAndroids](https://github.com/JakeWharton/NineOldAndroids) for view animations - all from [Jake Wharton](http://jakewharton.com/). -* [RoboGuice](http://code.google.com/p/roboguice/) for dependency-injection. +* [Dagger](https://github.com/square/dagger) for dependency-injection. +* [ButterKnife](https://github.com/JakeWharton/butterknife) for view injection +* [Otto](https://github.com/square/otto) as the event bus * [Robotium](http://code.google.com/p/robotium/) for driving our app during integration tests. * [android-maven-plugin](https://github.com/jayway/maven-android-plugin) From fe207e20aefc068dff9673bc5b445467c08611f1 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Fri, 29 Mar 2013 13:36:19 -0700 Subject: [PATCH 38/68] Remvoing old RG code that was missed. Adding butter knife to proguard, and udpating version of Butter knife --- app/pom.xml | 2 +- .../android/bootstrap/test/TestUserAccountUtil.java | 2 +- proguard.cfg | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index c62b8b1..bc08cb7 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -45,7 +45,7 @@ com.jakewharton butterknife - 1.2.2 + 1.3.0 com.actionbarsherlock diff --git a/integration-tests/src/main/java/com/donnfelker/android/bootstrap/test/TestUserAccountUtil.java b/integration-tests/src/main/java/com/donnfelker/android/bootstrap/test/TestUserAccountUtil.java index f3c6c12..9c034b4 100644 --- a/integration-tests/src/main/java/com/donnfelker/android/bootstrap/test/TestUserAccountUtil.java +++ b/integration-tests/src/main/java/com/donnfelker/android/bootstrap/test/TestUserAccountUtil.java @@ -10,8 +10,8 @@ import static com.donnfelker.android.bootstrap.core.Constants.Auth.*; import com.donnfelker.android.bootstrap.tests.R; +import com.donnfelker.android.bootstrap.util.Ln; -import roboguice.util.Ln; /** * Utilities for verifying an account diff --git a/proguard.cfg b/proguard.cfg index d59730c..06c61f0 100644 --- a/proguard.cfg +++ b/proguard.cfg @@ -36,7 +36,6 @@ -keepclassmembers class * { @com.google.inject.Provides *; @android.test.suitebuilder.annotation.* *; void test*(...); } --keep public class roboguice.** -keep class com.google.inject.Binder -keep class com.google.inject.Key -keep class com.google.inject.Provider @@ -76,3 +75,6 @@ @com.squareup.otto.Subscribe public *; @com.squareup.otto.Produce public *; } + +-dontwarn butterknife.Views$InjectViewProcessor +-keepclassmembers class **$$ViewInjector \ No newline at end of file From b66478d3c2f881e3c352aac7127acbec91557f4e Mon Sep 17 00:00:00 2001 From: Jon Willis Date: Tue, 2 Apr 2013 23:30:14 -0400 Subject: [PATCH 39/68] Replace straggling calls to Log.x() with Ln.x(). --- .../authenticator/BootstrapAccountAuthenticator.java | 4 +--- .../BootstrapAuthenticatorActivity.java | 2 +- .../bootstrap/authenticator/LogoutService.java | 7 +++---- .../android/bootstrap/core/AvatarLoader.java | 9 +++------ .../android/bootstrap/core/ImageUtils.java | 12 +++++------- .../android/bootstrap/util/SafeAsyncTask.java | 9 ++++++--- 6 files changed, 19 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAccountAuthenticator.java b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAccountAuthenticator.java index 1913d2b..8bc31f9 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAccountAuthenticator.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAccountAuthenticator.java @@ -16,8 +16,6 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.util.Log; - import com.donnfelker.android.bootstrap.core.Constants; class BootstrapAccountAuthenticator extends AbstractAccountAuthenticator { @@ -73,7 +71,7 @@ public Bundle editProperties(AccountAuthenticatorResponse response, String accou public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException { - Log.d("AccountAuthenticator", "Attempting to get authToken"); + Ln.d("Attempting to get authToken"); String authToken = AccountManager.get(context).peekAuthToken(account, authTokenType); Bundle bundle = new Bundle(); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAuthenticatorActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAuthenticatorActivity.java index b88f934..34e3bee 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAuthenticatorActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAuthenticatorActivity.java @@ -247,7 +247,7 @@ public Boolean call() throws Exception { .header(HEADER_PARSE_REST_API_KEY, PARSE_REST_API_KEY); - Log.d("Auth", "response=" + request.code()); + Ln.d("Authentication response=%s", request.code()); if(request.ok()) { final User model = new Gson().fromJson(Strings.toString(request.buffer()), User.class); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/LogoutService.java b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/LogoutService.java index 4a2d74c..9df81d5 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/LogoutService.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/LogoutService.java @@ -4,9 +4,8 @@ import android.accounts.AccountManager; import android.accounts.AccountManagerFuture; import android.content.Context; -import android.util.Log; - import com.donnfelker.android.bootstrap.core.Constants; +import com.donnfelker.android.bootstrap.util.Ln; import com.donnfelker.android.bootstrap.util.SafeAsyncTask; import javax.inject.Inject; @@ -62,7 +61,7 @@ public Boolean call() throws Exception { protected void onSuccess(Boolean accountWasRemoved) throws Exception { super.onSuccess(accountWasRemoved); - Log.d("LOGOUT_SERVICE", "Logout succeeded:" + accountWasRemoved); + Ln.d("Logout succeeded: %s", accountWasRemoved); onSuccess.run(); } @@ -70,7 +69,7 @@ protected void onSuccess(Boolean accountWasRemoved) throws Exception { @Override protected void onException(Exception e) throws RuntimeException { super.onException(e); - Log.e("LOGOUT_SERVICE", "Logout failed.", e.getCause()); + Ln.e(e.getCause(), "Logout failed."); } } } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/AvatarLoader.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/AvatarLoader.java index 508e0d4..d57e2ff 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/core/AvatarLoader.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/AvatarLoader.java @@ -9,11 +9,10 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.text.TextUtils; -import android.util.Log; import android.widget.ImageView; - import com.actionbarsherlock.app.ActionBar; import com.donnfelker.android.bootstrap.R; +import com.donnfelker.android.bootstrap.util.Ln; import com.donnfelker.android.bootstrap.util.SafeAsyncTask; import com.github.kevinsawicki.http.HttpRequest; import javax.inject.Inject; @@ -33,8 +32,6 @@ */ public class AvatarLoader { - private static final String TAG = "AvatarLoader"; - private static final float CORNER_RADIUS_IN_DIP = 3; private static final int CACHE_SIZE = 75; @@ -51,7 +48,7 @@ private FetchAvatarTask(Context context) { @Override protected void onException(Exception e) throws RuntimeException { - Log.d(TAG, "Avatar load failed", e); + Ln.d(e, "Avatar load failed"); } } @@ -189,7 +186,7 @@ protected BitmapDrawable fetchAvatar(final String url, final String userId) { else return null; } catch (IOException e) { - Log.d(TAG, "Exception writing rounded avatar", e); + Ln.d(e, "Exception writing rounded avatar"); return null; } finally { if (output != null) diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/ImageUtils.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/ImageUtils.java index 08f9543..f1d11fb 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/core/ImageUtils.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/ImageUtils.java @@ -12,8 +12,8 @@ import android.graphics.Point; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; -import android.util.Log; import android.widget.ImageView; +import com.donnfelker.android.bootstrap.util.Ln; import java.io.File; import java.io.IOException; @@ -24,8 +24,6 @@ */ public class ImageUtils { - private static final String TAG = "ImageUtils"; - /** * Get a bitmap from the image path * @@ -54,14 +52,14 @@ public static Bitmap getBitmap(final String imagePath, int sampleSize) { return BitmapFactory.decodeFileDescriptor(file.getFD(), null, options); } catch (IOException e) { - Log.d(TAG, e.getMessage(), e); + Ln.d(e, "Could not get cached bitmap."); return null; } finally { if (file != null) try { file.close(); } catch (IOException e) { - Log.d(TAG, e.getMessage(), e); + Ln.d(e, "Could not get cached bitmap."); } } } @@ -82,14 +80,14 @@ public static Point getSize(final String imagePath) { BitmapFactory.decodeFileDescriptor(file.getFD(), null, options); return new Point(options.outWidth, options.outHeight); } catch (IOException e) { - Log.d(TAG, e.getMessage(), e); + Ln.d(e, "Could not get size."); return null; } finally { if (file != null) try { file.close(); } catch (IOException e) { - Log.d(TAG, e.getMessage(), e); + Ln.d(e, "Could not get size."); } } } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/util/SafeAsyncTask.java b/app/src/main/java/com/donnfelker/android/bootstrap/util/SafeAsyncTask.java index e96f9f1..b5772d2 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/util/SafeAsyncTask.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/util/SafeAsyncTask.java @@ -2,12 +2,15 @@ import android.os.Handler; import android.os.Looper; -import android.util.Log; import java.io.InterruptedIOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.concurrent.*; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; /** * Originally from RoboGuice: https://github.com/roboguice/roboguice/blob/master/roboguice/src/main/java/roboguice/util/SafeAsyncTask.java @@ -143,7 +146,7 @@ protected void onException( Exception e ) throws RuntimeException { } protected void onThrowable( Throwable t ) throws RuntimeException { - Log.e("roboguice", "Throwable caught during background processing", t); + Ln.e(t, "Throwable caught during background processing"); } /** From 5dede7e7e4ab9bb05243023f050928612b057def Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Tue, 30 Apr 2013 13:34:25 -0700 Subject: [PATCH 40/68] Fixing issue where injection was in the wrong place and upgrading butterknife. --- app/pom.xml | 2 +- .../android/bootstrap/ui/BootstrapActivity.java | 11 ++++++++--- .../bootstrap/ui/BootstrapFragmentActivity.java | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index bc08cb7..54fb770 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -45,7 +45,7 @@ com.jakewharton butterknife - 1.3.0 + 1.3.2 com.actionbarsherlock diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapActivity.java index 7a1651c..6f287af 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapActivity.java @@ -3,6 +3,7 @@ import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP; import android.content.Intent; +import android.os.Bundle; import com.actionbarsherlock.app.SherlockActivity; import com.actionbarsherlock.view.MenuItem; @@ -16,11 +17,15 @@ public abstract class BootstrapActivity extends SherlockActivity { @Override - public void setContentView(int layoutResId) { - super.setContentView(layoutResId); - + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); BootstrapApplication.getInstance().inject(this); + } + + @Override + public void setContentView(int layoutResId) { + super.setContentView(layoutResId); // Used to inject views with the Butterknife library Views.inject(this); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java index dc23136..9abce2c 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java @@ -11,6 +11,7 @@ * Base class for all Bootstrap Activities that need fragments. */ public class BootstrapFragmentActivity extends SherlockFragmentActivity { + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); From 7ccc11a78755bd2f76ef2e9e1bd69de529161650 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Mon, 13 May 2013 05:21:20 -0700 Subject: [PATCH 41/68] Upgrading to Dagger 1.0.0 closes #44 --- app/pom.xml | 2 +- .../java/com/donnfelker/android/bootstrap/AndroidModule.java | 3 ++- .../java/com/donnfelker/android/bootstrap/BootstrapModule.java | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 54fb770..47f99be 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -17,7 +17,7 @@ 4.2.0 - 1.0-SNAPSHOT + 1.0.0 diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/AndroidModule.java b/app/src/main/java/com/donnfelker/android/bootstrap/AndroidModule.java index 903e8cd..9929597 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/AndroidModule.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/AndroidModule.java @@ -21,7 +21,8 @@ */ @Module ( - complete = false + complete = false, + library = true ) public class AndroidModule { diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java index 8e91be0..8dd73e1 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapModule.java @@ -30,7 +30,7 @@ ( complete = false, - entryPoints = { + injects = { BootstrapApplication.class, BootstrapAuthenticatorActivity.class, CarouselActivity.class, From 255a0289145e6d79ec8c53f857b34168ee59a294 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Mon, 13 May 2013 05:58:26 -0700 Subject: [PATCH 42/68] Updating ActionBarSherlock to 4.3.1 --- app/pom.xml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 47f99be..1ea10f9 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -16,7 +16,7 @@ - 4.2.0 + 4.3.1 1.0.0 @@ -47,19 +47,13 @@ butterknife 1.3.2 - - com.actionbarsherlock - library - ${abs.version} - apklib - com.actionbarsherlock actionbarsherlock ${abs.version} - jar - provided + apklib + com.github.kevinsawicki http-request From 473d79e8099ca2de1343069d35de6e78ef66abee Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Mon, 13 May 2013 06:02:09 -0700 Subject: [PATCH 43/68] Updating gson --- app/pom.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/pom.xml b/app/pom.xml index 1ea10f9..bb4abac 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -18,7 +18,9 @@ 4.3.1 1.0.0 + 2.2.3 + com.google.android @@ -29,7 +31,7 @@ com.google.code.gson gson - 2.2.2 + ${gson.version} com.squareup.dagger From 504b45c4c527e0e11d3a1b66acb33fccd181e3fd Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Mon, 13 May 2013 06:05:48 -0700 Subject: [PATCH 44/68] Upgrading Otto to 1.3.3 --- app/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pom.xml b/app/pom.xml index bb4abac..5bb2c59 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -86,7 +86,7 @@ com.squareup otto - 1.3.2 + 1.3.3 junit From 727762a5302b82c6eba480a8699b25c873395fe3 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Mon, 13 May 2013 06:06:28 -0700 Subject: [PATCH 45/68] moving version number to property --- app/pom.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/pom.xml b/app/pom.xml index 5bb2c59..bdb8866 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -18,6 +18,7 @@ 4.3.1 1.0.0 + 1.3.3 2.2.3 @@ -86,7 +87,7 @@ com.squareup otto - 1.3.3 + ${otto.version} junit From e82a0fb590b318288d2e9d43366e40eb57730b09 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Mon, 13 May 2013 09:59:06 -0700 Subject: [PATCH 46/68] Adding maven resources version. Closes #26 --- integration-tests/pom.xml | 1 + pom.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 183072f..1a9fd26 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -73,6 +73,7 @@ + ${maven.resources.version} com.jayway.maven.plugins.android.generation2 diff --git a/pom.xml b/pom.xml index 0c48910..f204d56 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,7 @@ UTF-8 4.1.1.4 + 2.6 From 7d7b0606f56433416109d18c7f3866a5f10f70aa Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Mon, 13 May 2013 10:05:19 -0700 Subject: [PATCH 47/68] Fixing missing import statement. --- .../bootstrap/authenticator/BootstrapAccountAuthenticator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAccountAuthenticator.java b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAccountAuthenticator.java index 8bc31f9..3fd3a73 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAccountAuthenticator.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/authenticator/BootstrapAccountAuthenticator.java @@ -17,6 +17,7 @@ import android.content.Intent; import android.os.Bundle; import com.donnfelker.android.bootstrap.core.Constants; +import com.donnfelker.android.bootstrap.util.Ln; class BootstrapAccountAuthenticator extends AbstractAccountAuthenticator { From 2340791f6766f402fb787aa1a1e1a2c1998428e6 Mon Sep 17 00:00:00 2001 From: joeykrim Date: Sat, 15 Jun 2013 17:02:11 -0400 Subject: [PATCH 48/68] Small typo "Some fo the..." changed to "Some of the..." --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index eb5cf27..ab0437d 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ to report any bugs or feature requests and to see the list of known issues. Have a questions about Android Bootstrap? Ask away on the [android-bootstrap discussion forum](https://groups.google.com/forum/#!forum/android-bootstrap). - + @@ -95,7 +95,7 @@ you will then be able to install your own built version. Android Bootstrap is a result of a template project I've developed over the years as well as a combination of a lot of great work that the [GitHub Gaug.es](http://www.github.com/github/gauges-android) -app and [GitHub Android](http://www.github.com/github/android) app showcased. Some fo the +app and [GitHub Android](http://www.github.com/github/android) app showcased. Some of the code in this project is based on the GitHub Gaug.es and GitHub Android app. Android Bootstrap is built on the awesome [Parse.com API](http://www.parse.com/) From 4d67b0b51d3d580206e98719bf0f7263482e3061 Mon Sep 17 00:00:00 2001 From: Derick Leony Date: Sun, 30 Jun 2013 17:52:51 +0200 Subject: [PATCH 49/68] Keeping methods in classes generated by butterknife. --- proguard.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proguard.cfg b/proguard.cfg index 06c61f0..b1da0dd 100644 --- a/proguard.cfg +++ b/proguard.cfg @@ -77,4 +77,4 @@ } -dontwarn butterknife.Views$InjectViewProcessor --keepclassmembers class **$$ViewInjector \ No newline at end of file +-keepclassmembers class **$$ViewInjector {*;} From 325a708be10c99467d9b89d8b41208685590c653 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Sun, 7 Jul 2013 21:07:10 -0700 Subject: [PATCH 50/68] Upgrading android-maven-plugin to 3.6.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f204d56..b8791c6 100644 --- a/pom.xml +++ b/pom.xml @@ -54,7 +54,7 @@ com.jayway.maven.plugins.android.generation2 android-maven-plugin - 3.4.0 + 3.6.0 true From 4c2b9a61308544bfe2f3283f21d92f5417676c47 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Sun, 7 Jul 2013 21:10:52 -0700 Subject: [PATCH 51/68] Upgrading GSON --- app/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pom.xml b/app/pom.xml index bdb8866..a182446 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -19,7 +19,7 @@ 4.3.1 1.0.0 1.3.3 - 2.2.3 + 2.2.4 From 260d4b3e7390b2da8bb67543e4dcbcd803ee8141 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Sun, 7 Jul 2013 21:14:25 -0700 Subject: [PATCH 52/68] Uprgrading Otto --- app/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pom.xml b/app/pom.xml index a182446..6c5f261 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -18,7 +18,7 @@ 4.3.1 1.0.0 - 1.3.3 + 1.3.4 2.2.4 From 35334c105397d4f99383cf96d309c67a9e734f3d Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Sun, 7 Jul 2013 21:22:13 -0700 Subject: [PATCH 53/68] Upgrading dagger --- app/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pom.xml b/app/pom.xml index 6c5f261..2b05bd2 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -17,7 +17,7 @@ 4.3.1 - 1.0.0 + 1.0.1 1.3.4 2.2.4 From 087d1f0ebb1c44b8ace4ba518d67703e875fb884 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Sun, 7 Jul 2013 21:27:28 -0700 Subject: [PATCH 54/68] Upgrading http-request --- app/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pom.xml b/app/pom.xml index 2b05bd2..999c1bf 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -60,7 +60,7 @@ com.github.kevinsawicki http-request - 3.0 + 5.4 com.viewpagerindicator From c2a007462033235ec96c96060c5a963435c6bd24 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Sun, 7 Jul 2013 21:30:11 -0700 Subject: [PATCH 55/68] Upgrading wishlist --- app/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/pom.xml b/app/pom.xml index 999c1bf..7d5ea7a 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -81,7 +81,7 @@ com.github.kevinsawicki wishlist - 0.8 + 0.9 apklib From 25f75b53b8dea618fe44e26c6671be0bac880763 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Sun, 7 Jul 2013 23:00:45 -0700 Subject: [PATCH 56/68] Adding sliding menu from net.simonvt.menudrawer --- app/assets/fonts/Roboto-Regular.ttf | Bin 0 -> 158604 bytes app/pom.xml | 8 ++- app/res/color/nav_text_selector.xml | 6 ++ app/res/drawable-hdpi/ic_drawer.png | Bin 0 -> 963 bytes app/res/drawable-hdpi/ic_home.png | Bin 0 -> 646 bytes app/res/drawable-hdpi/ic_timer.png | Bin 0 -> 1047 bytes app/res/drawable-mdpi/ic_drawer.png | Bin 0 -> 955 bytes app/res/drawable-mdpi/ic_home.png | Bin 0 -> 464 bytes app/res/drawable-mdpi/ic_timer.png | Bin 0 -> 642 bytes app/res/drawable-xhdpi/ic_drawer.png | Bin 0 -> 979 bytes app/res/drawable-xhdpi/ic_home.png | Bin 0 -> 919 bytes app/res/drawable-xhdpi/ic_timer.png | Bin 0 -> 1570 bytes app/res/drawable-xxhdpi/ic_home.png | Bin 0 -> 1675 bytes app/res/drawable-xxhdpi/ic_timer.png | Bin 0 -> 2670 bytes .../nav_menu_button_background_disabled.xml | 7 ++ .../nav_menu_button_background_enabled.xml | 8 +++ .../nav_menu_button_background_pressed.xml | 8 +++ .../nav_menu_button_background_selector.xml | 9 +++ app/res/layout/navigation_drawer.xml | 57 +++++++++++++++++ app/res/values/colors.xml | 7 +- app/res/values/strings.xml | 1 + app/res/values/styles.xml | 5 ++ .../bootstrap/ui/CarouselActivity.java | 59 +++++++++++++---- .../ui/view/CapitalizedTextView.java | 60 ++++++++++++++++++ 24 files changed, 221 insertions(+), 14 deletions(-) create mode 100755 app/assets/fonts/Roboto-Regular.ttf create mode 100644 app/res/color/nav_text_selector.xml create mode 100644 app/res/drawable-hdpi/ic_drawer.png create mode 100755 app/res/drawable-hdpi/ic_home.png create mode 100755 app/res/drawable-hdpi/ic_timer.png create mode 100644 app/res/drawable-mdpi/ic_drawer.png create mode 100755 app/res/drawable-mdpi/ic_home.png create mode 100755 app/res/drawable-mdpi/ic_timer.png create mode 100644 app/res/drawable-xhdpi/ic_drawer.png create mode 100755 app/res/drawable-xhdpi/ic_home.png create mode 100755 app/res/drawable-xhdpi/ic_timer.png create mode 100755 app/res/drawable-xxhdpi/ic_home.png create mode 100755 app/res/drawable-xxhdpi/ic_timer.png create mode 100755 app/res/drawable/nav_menu_button_background_disabled.xml create mode 100755 app/res/drawable/nav_menu_button_background_enabled.xml create mode 100755 app/res/drawable/nav_menu_button_background_pressed.xml create mode 100755 app/res/drawable/nav_menu_button_background_selector.xml create mode 100644 app/res/layout/navigation_drawer.xml create mode 100644 app/src/main/java/com/donnfelker/android/bootstrap/ui/view/CapitalizedTextView.java diff --git a/app/assets/fonts/Roboto-Regular.ttf b/app/assets/fonts/Roboto-Regular.ttf new file mode 100755 index 0000000000000000000000000000000000000000..7d9a6c4c32d7e920b549caf531e390733496b6e0 GIT binary patch literal 158604 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
v%Q*Qgy;tdJj4ZONjTx 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

sDMz7$^SDZV4x`K*fov0l~+M89O^^Om^v{t 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 literal 0 HcmV?d00001 diff --git a/app/pom.xml b/app/pom.xml index 7d5ea7a..b5861a2 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -20,6 +20,7 @@ 1.0.1 1.3.4 2.2.4 + 3.0.2 @@ -56,7 +57,12 @@ ${abs.version} apklib - + + net.simonvt.menudrawer + menudrawer + ${menudrawer.version} + apklib + com.github.kevinsawicki http-request diff --git a/app/res/color/nav_text_selector.xml b/app/res/color/nav_text_selector.xml new file mode 100644 index 0000000..2fcef81 --- /dev/null +++ b/app/res/color/nav_text_selector.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/res/drawable-hdpi/ic_drawer.png b/app/res/drawable-hdpi/ic_drawer.png new file mode 100644 index 0000000000000000000000000000000000000000..ec8c51f87f7db9cc7556a95a757716b114a51dda GIT binary patch literal 963 zcmaJ=zi-n(78Qu~a3!A@8Z z8$$;E0))CSF|sfe7Nm}>ObiTcObDbRm2;Y;3;!>rf# z^akCpr)%{x{eOwoUv%3f)i!D3E{UxWF*^?KAW-wH9%>-V89h2dWroSHZmUh&=6%J+ z9%rQ(F7X1&W|(p%2`qbn2#OYdcgHMn9hH5MH=!A5sPFELL)0Aax9su2E;(#v z8it{W9ExmD%&R{PfeL@`Sd z*=XbwE6MuN)?7hHksZ1Laj_3lMXQ5{L}h8D=P7u>Le`IFWugV+6D!~a4yGy1fu{L? zsOK%v5ow@{c>gIJwMGHr8z{oV(58*+Zl$gQMGKKdaM;55a9+iF9}^t)aR9Vt9+;Nx z`l&s|o2F9pBVzeBs_80A6*$**6eC}hBq-;0Ww9y#wWaY}n%U5@gPoAD0e0%u5 u__g`z^`)atPdb`>{LnXUPBI(JDrB~wT)lnz;^*7+S=Flh`kUQ@XMX?__$)^N literal 0 HcmV?d00001 diff --git a/app/res/drawable-hdpi/ic_home.png b/app/res/drawable-hdpi/ic_home.png new file mode 100755 index 0000000000000000000000000000000000000000..eb11224b6b618bf30db2d3ed88312fcc76021a37 GIT binary patch literal 646 zcmV;10(t$3P)`T{oVI@&Y2mnMn|52Cl22P1j2TLKnO&!DX1gV5$cH0 zg;LUxG$aj4quef99wCpAN5~`O5fOfCkXgzsWtP9wEOWVB7nyx{o_AZwkZY4XZ}DY( z!|SIJq+an`s_GFG+)L*B#5<9p*h^Ch*ROf#^i{RRYx5OX8DU`6Kv3);`Rx*|HqS>M zrzrUR-+-V{bGJ{3rohd5=DeTSOMOQGp~uRhtwhJ(alJm^5$Tc!5c({Hf*c~KH~*IO zfB?cU1?>>cQL9gtSpvWq@;I?UG*kc~9UTFP87f&N>MDd_h8?h)n7u_}N21Fo5QfP7 zkf@DW!5ia8fpHddo|3~80b%7Z#;0x)jgFFkVV-GV966e=Xd34rEQAc7vKCVgVfDUp z(d;m@0Li%o;uM61ut0(<4&+d(P9u|LV$UTE*B~ZHaE|cL+rhcnAjLVJ82sRy;FIU; zfXK#~)IZiKh_IySk_)bP$4vO|Z>R*qwtztV48m-(jsubz;e0VfMyZy@EM~XuFPiRI z;@m8|!9yS{5rGhhVsWS=)Dh~4^sX8M+}z-=)#iR*v3%YKmd|$1%^yC&LC>8Il(a(Tq!8TqY?zOmcY|VJ0$D;$aMuq?wVD z5K)qpkVp!p@E{KwzjdsmbN#-3#{QhXv3C92=j^@qch>rUYpuQZK8Zfok_;q_hu;hs z5$Xv>gb|UM6wD*cBg`Z6Zj=@p78({B78*fSwB-oP5tbt?M_7(1k=D+Fho`QO#cep2c3yT;=O}MdlUI*p#cG6_)bs>283AfgZZoA2!uTFW^KTO zA}0a@2J`!ezy{Dyb@@Pl3p@j#bZjX71TYT_*Rh}Ur@?Lr`4S}|krDw3<52z*n50JB zqdy9+VVJ!2oW>gQmc?KPn5hC!>8n`tA}TT>BSJ~If#RdUH?Rww!WenW$%f1@x(d+2 zc*-+UbK*T4!C^2EyafxQA|nzaG#qZLCrB79!vINw84bB|20B4G^FmrqLuNXISAc|z zc*g7~!Xzppno)QY5Kj;zb4XB+sG`3J#O*KVBSPhbj4t(zW@Kv%$@X;II}y4&+(jXA z?K+fh3NUajbFE+va%Y2#;JIS_Ng$y$%lB>)1rZlebOn&aI43_^E1VG3YW3o`3K3-as3{s40x}*KA|o!zvZ(Dd zWh};x;Iq^mGr&6_hpXq=Wa!B>7Tc%XJ~4swDAikdiOTbGoA2Q^``KQIEpi)yxsr1_bmhwfy!F0}OG1>3nM#Pbsn3nqp-Yu^ zI))cYi295&NRTj-YBGMua8+FHDMK%2f`~}X#rk4K$&mZXz^l3tkBI+OUE15U%iA9OO}~#@(U@%iBhz6 RnsopG002ovPDHLkV1n^F-A@1j literal 0 HcmV?d00001 diff --git a/app/res/drawable-mdpi/ic_drawer.png b/app/res/drawable-mdpi/ic_drawer.png new file mode 100644 index 0000000000000000000000000000000000000000..7420a68df97b5f14f3c4256f29e8658f2218ad03 GIT binary patch literal 955 zcmaJ=O^ee&7!KBzW!b`l7e5XTSv&~aWRh*#ZD=gJN#hnwDQ(3bJUC6JZRjQwlc`O6 zS`|S-5zqbtFM?+eiU+}yHxHit2ZA2F2zAnJst0ReGV>14^Sqz)xKY2B-MFy9aa^`` zXf)Zrm8|tM?0@OU46*Gzt#)XO^ynCcnA>+q7lWFI`nZXaGr98-mpLxOyX_9`Sl3mX zcmhf=LhJ>M&2i;Q93Xp$Dd^(9>udbCH*b01IvRgdv7i;`c;FsRL)@Cy+xB#5mmI!w z6_jI@33!+y5PL^{q{bRQ*Hzg$xfXdahtQ$Mp9Iyh8bBu@28sZ4HY`9;kOXNrUn~}P zfD9!lLRpmLoK#eIWfelO_;}_mbb4yjFc-d&s(D28e1`kq;kC74gS2C+vT z_G!v>mfRv-$xTMV3z%mEhwfeMm?81NJZ05g9gDG2Z^3m|#!{%RfrYM-yA|FuK)l5 literal 0 HcmV?d00001 diff --git a/app/res/drawable-mdpi/ic_home.png b/app/res/drawable-mdpi/ic_home.png new file mode 100755 index 0000000000000000000000000000000000000000..ad925f8747242a91f1d0d8015afb28bf022d4740 GIT binary patch literal 464 zcmV;>0WbcEP)YU6o(uCfv@2pE`s;~;tS~D;I5M*3MyDo5EOM31Qll&2X)fjF2&u=H&GPgcfu`% zSZ&TpOAe*Z^MBH{l?s|8_pQ;mhhJFmKbyoxZ#X}IF4oK z*(RUln#_>9Q1%i<(Rz`W(${S*z#am0Kw@%4TBIEyp8#I01Te6}fb5gw6p%pQljTYQ z13LoH8QCR6{}?Raa4>rcsT5ef(|h<6cJooEO+JZl043 z<>E4?X@xcc13S*5S7b#AE9LSrq;9-cq+B{1Lu=D z;ak*gg<3KF4)9mbu}PU4I6FDU-?%oNbJ22liKHQa17C7k9zu>;B0EUus3hMR0~cA`GS-1D z)-+?>u*R09)(u;%X~wu=jV(*98@5=}jB&#nTbA0|8U6vt;eRbj@4u}80000NklTYos=u4`pmAXCKxyyg^n?3? z-V68=dWLee&$>c81FN&E1pbJofcRU2pik0Zh_>%WTNBss(F7WCb=nD7bM}D1U(gE^ zRvrDV!270?yeH6Gs1J3z?@Sz6>#UTg*981z0hT&^hDPaM2>~9XeG#SO*6G*| z1KdrZujo0!Y(xHtzC$!k|GN-isTSL)hiAUUaXATKSwxQ1<;MV>SVp0yz%t~aDIsu- zLO-X^NUB8^=nE5?{@SI40)JO;x!l5q9rpLN8+h)D4a`jo7ja;;Dek@g?{+k~Ix^cFb z26W?W;4Q;{|NkP{CRtK#v6tqgK%#!v14nhr^*Qgbt8-D`^A1OK%Jn(#u&Z-X-}4Sf cb;|X*fAyvLEr%m9hX4Qo07*qoM6N<$f~>4FKmY&$ literal 0 HcmV?d00001 diff --git a/app/res/drawable-xhdpi/ic_drawer.png b/app/res/drawable-xhdpi/ic_drawer.png new file mode 100644 index 0000000000000000000000000000000000000000..7ffbe5c507bafb3f08c25d37dd002c5e486557d4 GIT binary patch literal 979 zcmaJ=L2J}N7)>pdQf-e~dvQp?lbcMkn@uw|t!=Vt7fdPJ1s6eRHko$AHkmk?b<@R@ zhl1eMUr>+g#iIumLGUPg@<%9wH-$Rc?rwY78kl@D!+Y<0-^@2px3@NCXBK7zL71&> z+BF_QKBlMmKkfB?^KhM28f=H`v93q4uojRe2Gz*h$2IH)hYvsFWkHw}!+L`?+`Gsp zk>uqVDT@-$7KG*1Eb;s{W}u1p!`KvmzI`WxFfhe?nhV{;!mV(#NAXT?tM2#Oz7dG4 zD_}W8Tp+^C16gzsrzkVUzAobPd|MVlAHv$EcxtNQZUc)@3^WNAd{~B{tVl|+r0e=E zph5-8P?Z(6py;TmA_&3Y6L~fo?4g?N407?6DYh6(kSuq*U8!4?2;G+z!!U9URV{Et zAw7(lmlfi4aj0P9)Td#>LK1^q(QA?pGez$7w1g-b$;Rp6nD~LonU~0l1oM)HK-c|0 zG>S&(l-2N=-?74J{V>6D4X30-eSUF!i+QR9SrmJW&^jRp!&BUD5k}G$Nr1JZ0oU`x zIA8biu8XR1%DmXeRofJ~f)s`UD#CT$Hq@ncU3Cr#`r@XDF+i#xpC$Ukr*Xr5#=HHW()sJ^B-8`C@n*1SL z1uGUVy3XgbNy9K0r#&^gCZrq*W8`>{V}zrc>y6}9cR?x{@w zbLYV1fCuZI|K!-J+c{k_?Lh!jxiNp?zFnG&&6*9_*vicl3b$=YWGlG2@VKS_z8SJ> zB+M^(rxtAAu);8FX;NXzzNKvrb&99*dt@aHb`(x4=xsEyR%>R$hXG1$7FZ@UOK^>>3woy;p1hmCT&wGQ;=u*QP??GjB#1@DQL!6W zSbSewRd06f|D*}#qK@k;-m9FDPfu1aekKv~)%8HX^wYwau$+At3^puU|I3Is=Rp{g zYu%Gq`w|y5&o}u0%tW&ARA_nj<>L=nKJeDPea!T5y{_T)1Dq<%+bumLcl5?3t!ota z$ZeCr&D1;a@%+hHQ)l;`7drN@4vWn&Ict0RPJ8EiMQI~(y{Y1xcCGwB=S}s;TSw! L{an^LB{Ts5d=IVn literal 0 HcmV?d00001 diff --git a/app/res/drawable-xhdpi/ic_timer.png b/app/res/drawable-xhdpi/ic_timer.png new file mode 100755 index 0000000000000000000000000000000000000000..0e15946c862285e710e883515cd87d495ad0e2eb GIT binary patch literal 1570 zcmcJP`#aMM0LH&F?JKo0=1^{HN=eQXmK-*h?MRqg2)RBY=GI6gEGze8IUbd=$ffmY zEDZnv-0D2haW60Z zD=FBXU(40i2Y`LERz$OlFK%QRp5RYt&N8^@6 zHAdo}36z&{o6~GgBBh)cPPtIcmUMUHrL1c~3?pSF$ASDj(||4HTZp0F?an2QBxpo{ zMBb>SBsktFd4|i7Lh$qU`$wxIRcLzh?F)E}ArPo}PZ&>(0J?3^9C7M@02pE^HC5G7 z5SD@#W4KC;vDj^xP!wGArmOQRdTRoqYg%`O$kGVJ=w(|VJ?95@roOhdYfhf?hw`t7 zj#OWRDpd@dO-cA`QT;Jbw<dTz-nruN7i=R-Ik9eb-MBAe= zCcrsqA(!4Ye3>iJUPY-kKq%EKnvaM156Y#f&k(YVGeTekW6Tr2T$4dN({2)KJ1%!4 zp*T^YmVvXXKh4JtzVcadeE|Hl*^P#Pods&T--jJcv*)u%t!rP*VkyC4VR9>FxO(N- z#QkXeyxhyc$ateQlu`rm1kni>C|cv^O64zhG; ziAm3Ae97Sfv9P4>dD?7IH2Z-%r;HH2DAoxj!7%rdrd^_7l|H(sw+!f%!M2wT#kPbK(0=Bd7Y; z5Ud|fMmgU*hS?X|9STaZ8THXhR1Mr&XGq8%ttkT~^CqJLYiH-|d2Chy8>&jyxz*u^ zyrv4}apqX9JJwMwjCQ4mi=Z2go&JX4^!ZH=y{<|(g7oXkJmb*YPds=({w$AFU^FWXJt~2KcN=!G| zJjSQYU~9JytQIFZes)nD|ETmn9`~ohL7O0_DcCs2-YXn7=3DNGxY5k`^bYf$t3ue~ zG7aIH(&G)8^LIZDh3)HxcUJ~qIa^HwnHW@M_!02%fP+_!y+Z?s3SH?XBcUdfL|McB z7zfg#l17=0DXGYBc2rRxpS*d5C1peIm!<+D`c8j)s;84f0_0xJdLIXV{ZRn&VPo%! z9OwYK(Y2xKQrEWGt}$Bi<4RUx%FU;~w44VWi!JPhSmnVQ=u5B$XEFWFU6q_%N~Kn^ zhQrmbiW0f|gs>mTKDk?z6l@7^sQB=P_4c#e%6f0>JvQ6imfBDSPW~8)lNBE7GbV6V z&v>`2JkW+ysc(6%`oar#;;1;x?hC!ha4Kp9UO`@dUgWHzD?H!y>Api#fi}8kkfkuz zMXXqx{7`qrpH3ESEP(aRjV~;;awz>AnrZc{K7%s+1v-TLEw@AGl+ZRSR4nH|_%^4E zwl`BkOjUZ&gs2SP>R4WTs;JrSk{e0YlC?ql6^zhmb619r;5?&@e%2vW1!`Yc#tI2q zAAyV5tce%&cMAdGAIA)7%mWcY9jS>&+TjfgW6>oO1wM0k@Yy>q6K*ZUq}(+(XoR#u ziY*Y$zu*ApYYnL2AVUysk^#W7j~tTp0of=4kZ<1QD^F9caJ=|`l-JcOvk%??kBg~$ P-w#-k$i!FXK1u%o6$0W% literal 0 HcmV?d00001 diff --git a/app/res/drawable-xxhdpi/ic_home.png b/app/res/drawable-xxhdpi/ic_home.png new file mode 100755 index 0000000000000000000000000000000000000000..547a4e151250bfbe93ef7d0d6ac90ddc655d32b0 GIT binary patch literal 1675 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 + + + + + + \ No newline at end of file diff --git a/app/res/drawable/nav_menu_button_background_enabled.xml b/app/res/drawable/nav_menu_button_background_enabled.xml new file mode 100755 index 0000000..97384c3 --- /dev/null +++ b/app/res/drawable/nav_menu_button_background_enabled.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/res/drawable/nav_menu_button_background_pressed.xml b/app/res/drawable/nav_menu_button_background_pressed.xml new file mode 100755 index 0000000..b6813e6 --- /dev/null +++ b/app/res/drawable/nav_menu_button_background_pressed.xml @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/app/res/drawable/nav_menu_button_background_selector.xml b/app/res/drawable/nav_menu_button_background_selector.xml new file mode 100755 index 0000000..f5a007b --- /dev/null +++ b/app/res/drawable/nav_menu_button_background_selector.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/res/layout/navigation_drawer.xml b/app/res/layout/navigation_drawer.xml new file mode 100644 index 0000000..a2c49be --- /dev/null +++ b/app/res/layout/navigation_drawer.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/res/values/colors.xml b/app/res/values/colors.xml index 5403e4d..066d62c 100644 --- a/app/res/values/colors.xml +++ b/app/res/values/colors.xml @@ -34,7 +34,12 @@ #7b7b7b #646464 #000000 - + #222222 #1fb6ed + #A6A6A6 + #5A5A5A + #252525 + #464646 + #2f2f2f \ No newline at end of file diff --git a/app/res/values/strings.xml b/app/res/values/strings.xml index e7a6b51..254797b 100644 --- a/app/res/values/strings.xml +++ b/app/res/values/strings.xml @@ -39,6 +39,7 @@ Pause Resume Timer + Home \ No newline at end of file diff --git a/app/res/values/styles.xml b/app/res/values/styles.xml index a6b885e..e8cc640 100644 --- a/app/res/values/styles.xml +++ b/app/res/values/styles.xml @@ -89,4 +89,9 @@ #464646 + + \ No newline at end of file diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java index 6cf6021..b9e3c8f 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java @@ -2,31 +2,30 @@ package com.donnfelker.android.bootstrap.ui; -import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; -import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP; import android.content.Intent; import android.os.Bundle; import android.support.v4.view.ViewPager; +import android.view.View; -import com.actionbarsherlock.view.Menu; import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.Window; -import com.donnfelker.android.bootstrap.BootstrapApplication; import com.donnfelker.android.bootstrap.R; -import com.donnfelker.android.bootstrap.R.id; - import com.viewpagerindicator.TitlePageIndicator; import butterknife.InjectView; import butterknife.Views; +import net.simonvt.menudrawer.MenuDrawer; + /** * Activity to view the carousel and view pager indicator with fragments. */ public class CarouselActivity extends BootstrapFragmentActivity { - @InjectView(id.tpi_header) TitlePageIndicator indicator; - @InjectView(id.vp_pages) ViewPager pager; + @InjectView(R.id.tpi_header) TitlePageIndicator indicator; + @InjectView(R.id.vp_pages) ViewPager pager; + + private MenuDrawer menuDrawer; @Override protected void onCreate(Bundle savedInstanceState) { @@ -34,23 +33,59 @@ protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); super.onCreate(savedInstanceState); - setContentView(R.layout.carousel_view); + + // Set up navigation drawer + menuDrawer = MenuDrawer.attach(this); + menuDrawer.setMenuView(R.layout.navigation_drawer); + menuDrawer.setContentView(R.layout.carousel_view); + menuDrawer.setSlideDrawable(R.drawable.ic_drawer); + menuDrawer.setDrawerIndicatorEnabled(true); + + Views.inject(this); pager.setAdapter(new BootstrapPagerAdapter(getResources(), getSupportFragmentManager())); indicator.setViewPager(pager); pager.setCurrentItem(1); + + setNavListeners(); + } + + private void setNavListeners() { + + menuDrawer.findViewById(R.id.home).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + menuDrawer.toggleMenu(); + } + }); + + menuDrawer.findViewById(R.id.timer).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + menuDrawer.toggleMenu(); + navigateToTimer(); + } + }); + } @Override public boolean onOptionsItemSelected(MenuItem item) { switch(item.getItemId()) { - case id.timer: - final Intent i = new Intent(this, BootstrapTimerActivity.class); - startActivity(i); + case android.R.id.home: + menuDrawer.toggleMenu(); + return true; + case R.id.timer: + navigateToTimer(); return true; default: return super.onOptionsItemSelected(item); } } + + private void navigateToTimer() { + final Intent i = new Intent(this, BootstrapTimerActivity.class); + startActivity(i); + } } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/view/CapitalizedTextView.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/view/CapitalizedTextView.java new file mode 100644 index 0000000..883e6ae --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/view/CapitalizedTextView.java @@ -0,0 +1,60 @@ +package com.donnfelker.android.bootstrap.ui.view; + +import android.content.Context; +import android.graphics.Typeface; +import android.os.Build; +import android.util.AttributeSet; +import android.widget.Button; + +import com.donnfelker.android.bootstrap.util.Strings; + +import java.util.Locale; + +/** + * A button who's text is always uppercase which uses the roboto font. + * Inspired by {@link com.actionbarsherlock.internal.widget.CapitalizingTextView} + */ +public class CapitalizedTextView extends Button { + + private static final boolean SANS_ICE_CREAM = Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH; + private static final boolean IS_GINGERBREAD = Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD; + + public CapitalizedTextView(Context context) { + super( context ); + + setTF( context ); + } + + public CapitalizedTextView(Context context, AttributeSet attrs) { + super( context, attrs ); + + setTF(context); + } + + public CapitalizedTextView(Context context, AttributeSet attrs, int defStyle) { + super( context, attrs, defStyle ); + + setTF(context); + + } + + @Override + public void setText(CharSequence text, BufferType type) { + if (IS_GINGERBREAD) { + try { + super.setText(text.toString().toUpperCase(Locale.ROOT), type); + } catch (NoSuchFieldError e) { + //Some manufacturer broke Locale.ROOT. See #572. + super.setText(text.toString().toUpperCase(), type); + } + } else { + super.setText(text.toString().toUpperCase(), type); + } + } + + private void setTF(Context context) { + setTypeface( Typeface.createFromAsset(context.getAssets(), "fonts/Roboto-Regular.ttf") ); + } + + +} From d41ad8697fedd4a8d8aebd670402d331b589c633 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Mon, 8 Jul 2013 13:38:05 -0700 Subject: [PATCH 57/68] Adjusting colors --- app/res/values/colors.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/res/values/colors.xml b/app/res/values/colors.xml index 066d62c..1cfe5bf 100644 --- a/app/res/values/colors.xml +++ b/app/res/values/colors.xml @@ -36,10 +36,10 @@ #000000 #222222 #1fb6ed - #A6A6A6 - #5A5A5A - #252525 + #ffffff + #1fb6ed + #646464 #464646 - #2f2f2f + #666b73 \ No newline at end of file From 307a4fa0d64fd54fe625767e27a34da9a34ee16c Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Mon, 8 Jul 2013 13:39:41 -0700 Subject: [PATCH 58/68] Updating readme to include menu drawer --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ab0437d..ff7611c 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,7 @@ and uses many great open-source libraries from the Android dev community: for swiping between fragments and [NineOldAndroids](https://github.com/JakeWharton/NineOldAndroids) for view animations - all from [Jake Wharton](http://jakewharton.com/). +* [MenuDrawer](https://github.com/SimonVT/android-menudrawer) for the menu drawer navigation. * [Dagger](https://github.com/square/dagger) for dependency-injection. * [ButterKnife](https://github.com/JakeWharton/butterknife) for view injection * [Otto](https://github.com/square/otto) as the event bus From 364db2cbeee910a8e7b47e3f565db789aa8496a4 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Mon, 8 Jul 2013 13:43:09 -0700 Subject: [PATCH 59/68] Updating readme, fixing typo. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ff7611c..6f6727f 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ to report any bugs or feature requests and to see the list of known issues. Have a questions about Android Bootstrap? Ask away on the [android-bootstrap discussion forum](https://groups.google.com/forum/#!forum/android-bootstrap). - + From 197e497288c13b27a8db22cd280e9b9108845634 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Mon, 8 Jul 2013 13:46:38 -0700 Subject: [PATCH 60/68] Updating readme with new images --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6f6727f..015ca95 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,11 @@ to report any bugs or feature requests and to see the list of known issues. Have a questions about Android Bootstrap? Ask away on the [android-bootstrap discussion forum](https://groups.google.com/forum/#!forum/android-bootstrap). - + + + + + ## HOW TO From e2cde33785216040668c0c8677e1d5bdeec6d7d2 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Wed, 10 Jul 2013 19:41:50 -0700 Subject: [PATCH 61/68] Adding zipaling into the build, updating the build number to match what is in Google Play and implementing fixes that closes #7 --- app/AndroidManifest.xml | 4 +- app/pom.xml | 1 + .../bootstrap/ui/CarouselActivity.java | 54 +++++++++++++++++-- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/app/AndroidManifest.xml b/app/AndroidManifest.xml index f26e711..a106b75 100644 --- a/app/AndroidManifest.xml +++ b/app/AndroidManifest.xml @@ -2,8 +2,8 @@ + android:versionCode="102" + android:versionName="1.1" > false + false true ${project.build.directory}/${project.artifactId}-${project.version}-signed-aligned.apk diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java index b9e3c8f..4403750 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CarouselActivity.java @@ -2,6 +2,7 @@ package com.donnfelker.android.bootstrap.ui; +import android.accounts.OperationCanceledException; import android.content.Intent; import android.os.Bundle; import android.support.v4.view.ViewPager; @@ -9,9 +10,14 @@ import com.actionbarsherlock.view.MenuItem; import com.actionbarsherlock.view.Window; +import com.donnfelker.android.bootstrap.BootstrapServiceProvider; import com.donnfelker.android.bootstrap.R; +import com.donnfelker.android.bootstrap.core.BootstrapService; +import com.donnfelker.android.bootstrap.util.SafeAsyncTask; import com.viewpagerindicator.TitlePageIndicator; +import javax.inject.Inject; + import butterknife.InjectView; import butterknife.Views; import net.simonvt.menudrawer.MenuDrawer; @@ -25,8 +31,12 @@ public class CarouselActivity extends BootstrapFragmentActivity { @InjectView(R.id.tpi_header) TitlePageIndicator indicator; @InjectView(R.id.vp_pages) ViewPager pager; + @Inject BootstrapServiceProvider serviceProvider; + private MenuDrawer menuDrawer; + private boolean userHasAuthenticated = false; + @Override protected void onCreate(Bundle savedInstanceState) { @@ -43,14 +53,52 @@ protected void onCreate(Bundle savedInstanceState) { Views.inject(this); - pager.setAdapter(new BootstrapPagerAdapter(getResources(), getSupportFragmentManager())); + checkAuth(); + + } + + private void initScreen() { + if(userHasAuthenticated) { + pager.setAdapter(new BootstrapPagerAdapter(getResources(), getSupportFragmentManager())); - indicator.setViewPager(pager); - pager.setCurrentItem(1); + indicator.setViewPager(pager); + pager.setCurrentItem(1); + + } setNavListeners(); } + private void checkAuth() { + new SafeAsyncTask() { + + @Override + public Boolean call() throws Exception { + final BootstrapService svc = serviceProvider.getService(CarouselActivity.this); + return svc != null; + + } + + @Override + protected void onException(Exception e) throws RuntimeException { + super.onException(e); + if(e instanceof OperationCanceledException) { + // User cancelled the authentication process (back button, etc). + // Since auth could not take place, lets finish this activity. + finish(); + } + } + + @Override + protected void onSuccess(Boolean hasAuthenticated) throws Exception { + super.onSuccess(hasAuthenticated); + userHasAuthenticated = true; + initScreen(); + } + }.execute(); + } + + private void setNavListeners() { menuDrawer.findViewById(R.id.home).setOnClickListener(new View.OnClickListener() { From 4ecd3b79d0b22901f6819f42c7ac17099b2bdc61 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Fri, 16 Aug 2013 16:36:04 -0700 Subject: [PATCH 62/68] Cleaning up the injection code a bit. --- .../bootstrap/BootstrapApplication.java | 15 ++----- .../android/bootstrap/Injector.java | 41 +++++++++++++++++++ .../android/bootstrap/core/TimerService.java | 3 +- .../bootstrap/ui/BootstrapActivity.java | 3 +- .../ui/BootstrapFragmentActivity.java | 3 +- .../bootstrap/ui/CheckInsListFragment.java | 3 +- .../bootstrap/ui/NewsListFragment.java | 3 +- .../bootstrap/ui/UserListFragment.java | 3 +- 8 files changed, 56 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/com/donnfelker/android/bootstrap/Injector.java diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java index e33a85f..5a735c4 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/BootstrapApplication.java @@ -18,7 +18,6 @@ public class BootstrapApplication extends Application { private static BootstrapApplication instance; - ObjectGraph objectGraph; /** * Create main application @@ -46,10 +45,9 @@ public void onCreate() { super.onCreate(); instance = this; - // Perform Injection - objectGraph = ObjectGraph.create(getRootModule()); - objectGraph.inject(this); - objectGraph.injectStatics(); + + // Perform injection + Injector.init(getRootModule(), this); } @@ -68,13 +66,6 @@ public BootstrapApplication(final Instrumentation instrumentation) { attachBaseContext(instrumentation.getTargetContext()); } - public void inject(Object object) - { - objectGraph.inject(object); - } - - - public static BootstrapApplication getInstance() { return instance; } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/Injector.java b/app/src/main/java/com/donnfelker/android/bootstrap/Injector.java new file mode 100644 index 0000000..937d8bd --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/Injector.java @@ -0,0 +1,41 @@ +package com.donnfelker.android.bootstrap; + +import dagger.ObjectGraph; + +public final class Injector +{ + public static ObjectGraph objectGraph = null; + + + public static void init(final Object rootModule) { + + if(objectGraph == null) + { + objectGraph = ObjectGraph.create(rootModule); + } + else + { + objectGraph = objectGraph.plus(rootModule); + } + + // Inject statics + objectGraph.injectStatics(); + + } + + public static void init(final Object rootModule, final Object target) + { + init(rootModule); + inject(target); + } + + public static final void inject(final Object target) + { + objectGraph.inject(target); + } + + public static T resolve(Class type) + { + return objectGraph.get(type); + } +} diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java index c210a12..3cef3a6 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/TimerService.java @@ -12,6 +12,7 @@ import android.support.v4.app.NotificationCompat; import com.donnfelker.android.bootstrap.BootstrapApplication; +import com.donnfelker.android.bootstrap.Injector; import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.ui.BootstrapTimerActivity; import javax.inject.Inject; @@ -46,7 +47,7 @@ public IBinder onBind(Intent intent) { public void onCreate() { super.onCreate(); - BootstrapApplication.getInstance().inject(this); + Injector.inject(this); // Register the bus so we can send notifications. BUS.register(this); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapActivity.java index 6f287af..03c4e2c 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapActivity.java @@ -8,6 +8,7 @@ import com.actionbarsherlock.app.SherlockActivity; import com.actionbarsherlock.view.MenuItem; import com.donnfelker.android.bootstrap.BootstrapApplication; +import com.donnfelker.android.bootstrap.Injector; import butterknife.Views; @@ -20,7 +21,7 @@ public abstract class BootstrapActivity extends SherlockActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - BootstrapApplication.getInstance().inject(this); + Injector.inject(this); } @Override diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java index 9abce2c..ef554b4 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/BootstrapFragmentActivity.java @@ -4,6 +4,7 @@ import com.actionbarsherlock.app.SherlockFragmentActivity; import com.donnfelker.android.bootstrap.BootstrapApplication; +import com.donnfelker.android.bootstrap.Injector; import butterknife.Views; @@ -16,7 +17,7 @@ public class BootstrapFragmentActivity extends SherlockFragmentActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - BootstrapApplication.getInstance().inject(this); + Injector.inject(this); } @Override diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java index 25f98f1..32241ef 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/CheckInsListFragment.java @@ -11,6 +11,7 @@ import com.donnfelker.android.bootstrap.BootstrapApplication; import com.donnfelker.android.bootstrap.BootstrapServiceProvider; +import com.donnfelker.android.bootstrap.Injector; import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.authenticator.LogoutService; import com.donnfelker.android.bootstrap.core.CheckIn; @@ -29,7 +30,7 @@ public class CheckInsListFragment extends ItemListFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - BootstrapApplication.getInstance().inject(this); + Injector.inject(this); } @Override diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java index 246e94a..206ea2b 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/NewsListFragment.java @@ -11,6 +11,7 @@ import com.donnfelker.android.bootstrap.BootstrapApplication; import com.donnfelker.android.bootstrap.BootstrapServiceProvider; +import com.donnfelker.android.bootstrap.Injector; import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.authenticator.LogoutService; import com.donnfelker.android.bootstrap.core.News; @@ -29,7 +30,7 @@ public class NewsListFragment extends ItemListFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - BootstrapApplication.getInstance().inject(this); + Injector.inject(this); } @Override diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java index 74ec7cc..ebb2b00 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java @@ -12,6 +12,7 @@ import com.donnfelker.android.bootstrap.BootstrapApplication; import com.donnfelker.android.bootstrap.BootstrapServiceProvider; +import com.donnfelker.android.bootstrap.Injector; import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.authenticator.LogoutService; import com.donnfelker.android.bootstrap.core.AvatarLoader; @@ -33,7 +34,7 @@ public class UserListFragment extends ItemListFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - BootstrapApplication.getInstance().inject(this); + Injector.inject(this); } @Override From 5e02afa65ab20c941b626595dde49b8586920486 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Sat, 17 Aug 2013 20:29:23 -0700 Subject: [PATCH 63/68] Using Picasso instead of avatar loader --- app/pom.xml | 5 + .../android/bootstrap/core/AvatarLoader.java | 405 ------------------ .../android/bootstrap/core/User.java | 15 + .../android/bootstrap/ui/UserActivity.java | 10 +- .../android/bootstrap/ui/UserListAdapter.java | 16 +- .../bootstrap/ui/UserListFragment.java | 9 +- 6 files changed, 34 insertions(+), 426 deletions(-) delete mode 100644 app/src/main/java/com/donnfelker/android/bootstrap/core/AvatarLoader.java diff --git a/app/pom.xml b/app/pom.xml index 9c7a353..4bafe97 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -57,6 +57,11 @@ ${abs.version} apklib + + com.squareup.picasso + picasso + 1.1.1 + net.simonvt.menudrawer menudrawer diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/AvatarLoader.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/AvatarLoader.java deleted file mode 100644 index d57e2ff..0000000 --- a/app/src/main/java/com/donnfelker/android/bootstrap/core/AvatarLoader.java +++ /dev/null @@ -1,405 +0,0 @@ -package com.donnfelker.android.bootstrap.core; - -import static android.graphics.Bitmap.CompressFormat.PNG; -import static android.graphics.Bitmap.Config.ARGB_8888; -import static android.view.View.VISIBLE; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.text.TextUtils; -import android.widget.ImageView; -import com.actionbarsherlock.app.ActionBar; -import com.donnfelker.android.bootstrap.R; -import com.donnfelker.android.bootstrap.util.Ln; -import com.donnfelker.android.bootstrap.util.SafeAsyncTask; -import com.github.kevinsawicki.http.HttpRequest; -import javax.inject.Inject; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicReference; - - -/** - * Avatar utilities - */ -public class AvatarLoader { - - private static final float CORNER_RADIUS_IN_DIP = 3; - - private static final int CACHE_SIZE = 75; - - private static abstract class FetchAvatarTask extends - SafeAsyncTask { - - private static final Executor EXECUTOR = Executors - .newFixedThreadPool(1); - - private FetchAvatarTask(Context context) { - super(EXECUTOR); - } - - @Override - protected void onException(Exception e) throws RuntimeException { - Ln.d(e, "Avatar load failed"); - } - } - - private final float cornerRadius; - - private final Map loaded = new LinkedHashMap( - CACHE_SIZE, 1.0F) { - - private static final long serialVersionUID = -4191624209581976720L; - - @Override - protected boolean removeEldestEntry( - Map.Entry eldest) { - return size() >= CACHE_SIZE; - } - }; - - private final Context context; - - private final File avatarDir; - - private final Drawable loadingAvatar; - - private final BitmapFactory.Options options; - - /** - * Create avatar helper - * - * @param context - */ - @Inject - public AvatarLoader(final Context context) { - this.context = context; - - loadingAvatar = context.getResources().getDrawable(R.drawable.gravatar_icon); - - avatarDir = new File(context.getCacheDir(), "avatars/" + context.getPackageName()); - if (!avatarDir.isDirectory()) - avatarDir.mkdirs(); - - float density = context.getResources().getDisplayMetrics().density; - cornerRadius = CORNER_RADIUS_IN_DIP * density; - - options = new BitmapFactory.Options(); - options.inDither = false; - options.inPreferredConfig = ARGB_8888; - } - - /** - * Get image for user - * - * @param user - * @return image - */ - protected BitmapDrawable getImage(final User user) { - File avatarFile = new File(avatarDir, user.getObjectId()); - - if (!avatarFile.exists() || avatarFile.length() == 0) - return null; - - Bitmap bitmap = decode(avatarFile); - if (bitmap != null) - return new BitmapDrawable(context.getResources(), bitmap); - else { - avatarFile.delete(); - return null; - } - } - -// /** -// * Get image for user -// * -// * @param user -// * @return image -// */ -// protected BitmapDrawable getImage(final CommitUser user) { -// File avatarFile = new File(avatarDir, user.getEmail()); -// -// if (!avatarFile.exists() || avatarFile.length() == 0) -// return null; -// -// Bitmap bitmap = decode(avatarFile); -// if (bitmap != null) -// return new BitmapDrawable(context.getResources(), bitmap); -// else { -// avatarFile.delete(); -// return null; -// } -// } - - /** - * Decode file to bitmap - * - * @param file - * @return bitmap - */ - protected Bitmap decode(final File file) { - return BitmapFactory.decodeFile(file.getAbsolutePath(), options); - } - - /** - * Fetch avatar from URL - * - * @param url - * @param userId - * @return bitmap - */ - protected BitmapDrawable fetchAvatar(final String url, final String userId) { - File rawAvatar = new File(avatarDir, userId + "-raw"); - HttpRequest request = HttpRequest.get(url); - if (request.ok()) - request.receive(rawAvatar); - - if (!rawAvatar.exists() || rawAvatar.length() == 0) - return null; - - Bitmap bitmap = decode(rawAvatar); - if (bitmap == null) { - rawAvatar.delete(); - return null; - } - - bitmap = ImageUtils.roundCorners(bitmap, cornerRadius); - if (bitmap == null) { - rawAvatar.delete(); - return null; - } - - File roundedAvatar = new File(avatarDir, userId.toString()); - FileOutputStream output = null; - try { - output = new FileOutputStream(roundedAvatar); - if (bitmap.compress(PNG, 100, output)) - return new BitmapDrawable(context.getResources(), bitmap); - else - return null; - } catch (IOException e) { - Ln.d(e, "Exception writing rounded avatar"); - return null; - } finally { - if (output != null) - try { - output.close(); - } catch (IOException e) { - // Ignored - } - rawAvatar.delete(); - } - } - - /** - * Sets the logo on the {@link com.actionbarsherlock.app.ActionBar} to the user's avatar. - * - * @param actionBar - * @param user - * @return this helper - */ - public AvatarLoader bind(final ActionBar actionBar, final User user) { - return bind(actionBar, new AtomicReference(user)); - } - - /** - * Sets the logo on the {@link ActionBar} to the user's avatar. - * - * @param actionBar - * @param userReference - * @return this helper - */ - public AvatarLoader bind(final ActionBar actionBar, - final AtomicReference userReference) { - if (userReference == null) - return this; - - final User user = userReference.get(); - if (user == null) - return this; - - final String avatarUrl = user.getAvatarUrl(); - if (TextUtils.isEmpty(avatarUrl)) - return this; - - final String userId = user.getObjectId(); - - BitmapDrawable loadedImage = loaded.get(userId); - if (loadedImage != null) { - actionBar.setLogo(loadedImage); - return this; - } - - new FetchAvatarTask(context) { - - @Override - public BitmapDrawable call() throws Exception { - final BitmapDrawable image = getImage(user); - if (image != null) - return image; - else - return fetchAvatar(avatarUrl, userId.toString()); - } - - @Override - protected void onSuccess(BitmapDrawable image) throws Exception { - final User current = userReference.get(); - if (current != null && userId.equals(current.getObjectId())) - actionBar.setLogo(image); - } - }.execute(); - - return this; - } - - private AvatarLoader setImage(final Drawable image, final ImageView view) { - return setImage(image, view, null); - } - - private AvatarLoader setImage(final Drawable image, final ImageView view, - Object tag) { - view.setImageDrawable(image); - view.setTag(R.id.iv_avatar, tag); - view.setVisibility(VISIBLE); - return this; - } - - private String getAvatarUrl(String id) { - if (!TextUtils.isEmpty(id)) - return "https://secure.gravatar.com/avatar/" + id + "?d=404"; - else - return null; - } - - private String getAvatarUrl(User user) { - String avatarUrl = user.getAvatarUrl(); - if (TextUtils.isEmpty(avatarUrl)) { - String gravatarId = user.getGravatarId(); - if (TextUtils.isEmpty(gravatarId)) - gravatarId = GravatarUtils.getHash(user.getUsername()); - avatarUrl = getAvatarUrl(gravatarId); - } - return avatarUrl; - } - -// private String getAvatarUrl(CommitUser user) { -// return getAvatarUrl(GravatarUtils.getHash(user.getEmail())); -// } - - /** - * Bind view to image at URL - * - * @param view - * @param user - * @return this helper - */ - public AvatarLoader bind(final ImageView view, final User user) { - if (user == null) - return setImage(loadingAvatar, view); - - String avatarUrl = getAvatarUrl(user); - - if (TextUtils.isEmpty(avatarUrl)) - return setImage(loadingAvatar, view); - - final String userId = user.getObjectId(); - - BitmapDrawable loadedImage = loaded.get(userId); - if (loadedImage != null) - return setImage(loadedImage, view); - - setImage(loadingAvatar, view, userId); - - final String loadUrl = avatarUrl; - new FetchAvatarTask(context) { - - @Override - public BitmapDrawable call() throws Exception { - if (!userId.equals(view.getTag(R.id.iv_avatar))) - return null; - - final BitmapDrawable image = getImage(user); - if (image != null) - return image; - else - return fetchAvatar(loadUrl, userId.toString()); - } - - @Override - protected void onSuccess(final BitmapDrawable image) - throws Exception { - if (image == null) - return; - loaded.put(userId, image); - if (userId.equals(view.getTag(R.id.iv_avatar))) - setImage(image, view); - } - - }.execute(); - - return this; - } - -// /** -// * Bind view to image at URL -// * -// * @param view -// * @param user -// * @return this helper -// */ -// public AvatarLoader bind(final ImageView view, final CommitUser user) { -// if (user == null) -// return setImage(loadingAvatar, view); -// -// String avatarUrl = getAvatarUrl(user); -// -// if (TextUtils.isEmpty(avatarUrl)) -// return setImage(loadingAvatar, view); -// -// final String userId = user.getEmail(); -// -// BitmapDrawable loadedImage = loaded.get(userId); -// if (loadedImage != null) -// return setImage(loadedImage, view); -// -// setImage(loadingAvatar, view, userId); -// -// final String loadUrl = avatarUrl; -// new FetchAvatarTask(context) { -// -// @Override -// public BitmapDrawable call() throws Exception { -// if (!userId.equals(view.getTag(id.iv_avatar))) -// return null; -// -// final BitmapDrawable image = getImage(user); -// if (image != null) -// return image; -// else -// return fetchAvatar(loadUrl, userId); -// } -// -// @Override -// protected void onSuccess(final BitmapDrawable image) -// throws Exception { -// if (image == null) -// return; -// loaded.put(userId, image); -// if (userId.equals(view.getTag(id.iv_avatar))) -// setImage(image, view); -// } -// -// }.execute(); -// -// return this; -// } -} - diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/User.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/User.java index 4dfcb44..9a29c1f 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/core/User.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/User.java @@ -1,5 +1,7 @@ package com.donnfelker.android.bootstrap.core; +import android.text.TextUtils; + import java.io.Serializable; public class User implements Serializable { @@ -70,6 +72,19 @@ public String getGravatarId() { } public String getAvatarUrl() { + if (TextUtils.isEmpty(avatarUrl)) { + String gravatarId = getGravatarId(); + if (TextUtils.isEmpty(gravatarId)) + gravatarId = GravatarUtils.getHash(getUsername()); + avatarUrl = getAvatarUrl(gravatarId); + } return avatarUrl; } + + private String getAvatarUrl(String id) { + if (!TextUtils.isEmpty(id)) + return "https://secure.gravatar.com/avatar/" + id + "?d=404"; + else + return null; + } } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserActivity.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserActivity.java index 6a3f964..e7437be 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserActivity.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserActivity.java @@ -5,22 +5,17 @@ import android.widget.ImageView; import android.widget.TextView; -import com.donnfelker.android.bootstrap.BootstrapApplication; import com.donnfelker.android.bootstrap.R; -import com.donnfelker.android.bootstrap.core.AvatarLoader; import com.donnfelker.android.bootstrap.core.User; -import javax.inject.Inject; +import com.squareup.picasso.Picasso; import butterknife.InjectView; -import butterknife.Views; public class UserActivity extends BootstrapActivity { @InjectView(R.id.iv_avatar) protected ImageView avatar; @InjectView(R.id.tv_name) protected TextView name; - @Inject protected AvatarLoader avatarLoader; - protected User user; @Override @@ -36,7 +31,8 @@ protected void onCreate(Bundle savedInstanceState) { getSupportActionBar().setHomeButtonEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true); - avatarLoader.bind(avatar, user); + Picasso.with(this).load(user.getAvatarUrl()).placeholder(R.drawable.gravatar_icon).into(avatar); + name.setText(String.format("%s %s", user.getFirstName(), user.getLastName())); } diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListAdapter.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListAdapter.java index 398a186..e3f8340 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListAdapter.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListAdapter.java @@ -3,10 +3,11 @@ import android.text.TextUtils; import android.view.LayoutInflater; +import com.donnfelker.android.bootstrap.BootstrapApplication; import com.donnfelker.android.bootstrap.R; -import com.donnfelker.android.bootstrap.core.AvatarLoader; import com.donnfelker.android.bootstrap.core.User; import com.github.kevinsawicki.wishlist.SingleTypeAdapter; +import com.squareup.picasso.Picasso; import java.text.SimpleDateFormat; import java.util.List; @@ -17,24 +18,22 @@ public class UserListAdapter extends SingleTypeAdapter { private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("MMMM dd"); - private final AvatarLoader avatars; /** * @param inflater * @param items */ - public UserListAdapter(LayoutInflater inflater, List items, AvatarLoader avatars) { + public UserListAdapter(LayoutInflater inflater, List items) { super(inflater, R.layout.user_list_item); - this.avatars = avatars; setItems(items); } /** * @param inflater */ - public UserListAdapter(LayoutInflater inflater, AvatarLoader avatars) { - this(inflater, null, avatars); + public UserListAdapter(LayoutInflater inflater) { + this(inflater, null); } @@ -53,7 +52,10 @@ protected int[] getChildViewIds() { @Override protected void update(int position, User user) { - avatars.bind(imageView(0), user); + Picasso.with(BootstrapApplication.getInstance()) + .load(user.getAvatarUrl()) + .placeholder(R.drawable.gravatar_icon) + .into(imageView(0)); setText(1, String.format("%1$s %2$s", user.getFirstName(), user.getLastName())); diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java index ebb2b00..523450a 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/UserListFragment.java @@ -1,6 +1,5 @@ package com.donnfelker.android.bootstrap.ui; -import static com.donnfelker.android.bootstrap.core.Constants.Extra.NEWS_ITEM; import static com.donnfelker.android.bootstrap.core.Constants.Extra.USER; import android.accounts.OperationCanceledException; import android.app.Activity; @@ -10,24 +9,20 @@ import android.view.View; import android.widget.ListView; -import com.donnfelker.android.bootstrap.BootstrapApplication; import com.donnfelker.android.bootstrap.BootstrapServiceProvider; import com.donnfelker.android.bootstrap.Injector; import com.donnfelker.android.bootstrap.R; import com.donnfelker.android.bootstrap.authenticator.LogoutService; -import com.donnfelker.android.bootstrap.core.AvatarLoader; -import com.donnfelker.android.bootstrap.core.News; import com.donnfelker.android.bootstrap.core.User; import com.github.kevinsawicki.wishlist.SingleTypeAdapter; -import javax.inject.Inject; +import javax.inject.Inject; import java.util.Collections; import java.util.List; public class UserListFragment extends ItemListFragment { @Inject BootstrapServiceProvider serviceProvider; - @Inject AvatarLoader avatars; @Inject LogoutService logoutService; @@ -110,6 +105,6 @@ protected int getErrorMessage(Exception exception) { @Override protected SingleTypeAdapter createAdapter(List items) { - return new UserListAdapter(getActivity().getLayoutInflater(), items, avatars); + return new UserListAdapter(getActivity().getLayoutInflater(), items); } } From 741964faaaff786a89d7b1877e3700d8ee1ebbed Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Tue, 3 Sep 2013 21:42:52 -0500 Subject: [PATCH 64/68] Update to sdk 17 and fix a bug so that source can build in Android Studio. --- .../donnfelker/android/bootstrap/core/IntentFactory.java | 4 ++++ .../android/bootstrap/ui/AlternatingColorListAdapter.java | 6 +++--- integration-tests/AndroidManifest.xml | 2 +- pom.xml | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/com/donnfelker/android/bootstrap/core/IntentFactory.java diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/core/IntentFactory.java b/app/src/main/java/com/donnfelker/android/bootstrap/core/IntentFactory.java new file mode 100644 index 0000000..1319121 --- /dev/null +++ b/app/src/main/java/com/donnfelker/android/bootstrap/core/IntentFactory.java @@ -0,0 +1,4 @@ +package com.donnfelker.android.bootstrap.core; + +public class IntentFactory { +} diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/AlternatingColorListAdapter.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/AlternatingColorListAdapter.java index 22a5b55..7446d65 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/AlternatingColorListAdapter.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/AlternatingColorListAdapter.java @@ -3,7 +3,7 @@ import android.view.LayoutInflater; -import com.actionbarsherlock.R.color; +import com.donnfelker.android.bootstrap.R; import com.github.kevinsawicki.wishlist.SingleTypeAdapter; import com.donnfelker.android.bootstrap.R.drawable; @@ -49,8 +49,8 @@ public AlternatingColorListAdapter(final int layoutId, primaryResource = drawable.table_background_selector; secondaryResource = drawable.table_background_alternate_selector; } else { - primaryResource = color.pager_background; - secondaryResource = color.pager_background_alternate; + primaryResource = R.color.pager_background; + secondaryResource = R.color.pager_background_alternate; } setItems(items); diff --git a/integration-tests/AndroidManifest.xml b/integration-tests/AndroidManifest.xml index 4d7733b..4471d30 100644 --- a/integration-tests/AndroidManifest.xml +++ b/integration-tests/AndroidManifest.xml @@ -2,7 +2,7 @@ - + diff --git a/pom.xml b/pom.xml index b8791c6..ed4cc6c 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ UTF-8 - 4.1.1.4 + 4.2.2 2.6 @@ -58,7 +58,7 @@ true - 16 + 17 From adcc7bba2217dc8eade077171488281989c954a1 Mon Sep 17 00:00:00 2001 From: Donn Felker Date: Wed, 4 Sep 2013 01:06:37 -0500 Subject: [PATCH 65/68] reverting SDK change --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index ed4cc6c..b8791c6 100644 --- a/pom.xml +++ b/pom.xml @@ -45,7 +45,7 @@ UTF-8 - 4.2.2 + 4.1.1.4 2.6 @@ -58,7 +58,7 @@ true - 17 + 16 From 437e13b5ab5006ccb37479297f683ff5a77c5e43 Mon Sep 17 00:00:00 2001 From: "Alexander.Iljushkin" Date: Tue, 10 Sep 2013 16:41:22 +0400 Subject: [PATCH 66/68] Fixed bug "Native typeface cannot be made" on som devices. --- .../ui/view/CapitalizedTextView.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/view/CapitalizedTextView.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/view/CapitalizedTextView.java index 883e6ae..eaabcd5 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/view/CapitalizedTextView.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/view/CapitalizedTextView.java @@ -19,6 +19,9 @@ public class CapitalizedTextView extends Button { private static final boolean SANS_ICE_CREAM = Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH; private static final boolean IS_GINGERBREAD = Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD; + private static final String TAG = "Typefaces"; + private static final Hashtable cache = new Hashtable(); + public CapitalizedTextView(Context context) { super( context ); @@ -52,9 +55,25 @@ public void setText(CharSequence text, BufferType type) { } } - private void setTF(Context context) { - setTypeface( Typeface.createFromAsset(context.getAssets(), "fonts/Roboto-Regular.ttf") ); + public static Typeface getTypeFace(Context c, String assetPath) { + synchronized (cache) { + if (!cache.containsKey(assetPath)) { + try { + Typeface t = Typeface.createFromAsset(c.getAssets(), + assetPath); + cache.put(assetPath, t); + } catch (Exception e) { + Log.e(TAG, "Could not get typeface '" + assetPath + + "' because " + e.getMessage()); + return null; + } + } + return cache.get(assetPath); + } } - + private void setTF(Context context) { + Typeface tf = getTypeFace(context, "fonts/Roboto-Regular.ttf"); + setTypeface( tf ); + } } From b521d18a307bee952a2cdb15c2860b1a578f8e27 Mon Sep 17 00:00:00 2001 From: sarpe Date: Tue, 19 Nov 2013 18:44:30 +0000 Subject: [PATCH 67/68] added missing HashTable and Log --- .../android/bootstrap/ui/view/CapitalizedTextView.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/donnfelker/android/bootstrap/ui/view/CapitalizedTextView.java b/app/src/main/java/com/donnfelker/android/bootstrap/ui/view/CapitalizedTextView.java index 883e6ae..1efd419 100644 --- a/app/src/main/java/com/donnfelker/android/bootstrap/ui/view/CapitalizedTextView.java +++ b/app/src/main/java/com/donnfelker/android/bootstrap/ui/view/CapitalizedTextView.java @@ -7,7 +7,8 @@ import android.widget.Button; import com.donnfelker.android.bootstrap.util.Strings; - +import android.util.Log; +import java.util.Hashtable; import java.util.Locale; /** From 22fabc86d77b84d6c63267eaf3f634776959aa5a Mon Sep 17 00:00:00 2001 From: sarpe Date: Wed, 20 Nov 2013 10:44:47 +0000 Subject: [PATCH 68/68] android-maven-plugin version 3.8.0 in order to have a working build --- app/pom.xml | 1 + pom.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/pom.xml b/app/pom.xml index 4bafe97..ee3868c 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -124,6 +124,7 @@ com.jayway.maven.plugins.android.generation2 android-maven-plugin + 3.8.0 com.squareup.dagger diff --git a/pom.xml b/pom.xml index b8791c6..f77e508 100644 --- a/pom.xml +++ b/pom.xml @@ -54,7 +54,7 @@ com.jayway.maven.plugins.android.generation2 android-maven-plugin - 3.6.0 + 3.8.0 true