Skip to content

Commit 0f1ec42

Browse files
Added offline support and some UI improvements
1 parent 5e64f2f commit 0f1ec42

File tree

14 files changed

+196
-50
lines changed

14 files changed

+196
-50
lines changed

app/build.gradle

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ android {
2020

2121
dependencies {
2222
compile fileTree(dir: 'libs', include: '*.jar')
23-
// compile 'com.android.support:appcompat-v7:21.0.0+'
24-
compile 'com.android.support:appcompat-v7:21.0.3'
23+
compile 'com.android.support:appcompat-v7:21.0.0+'
2524
compile 'com.squareup.picasso:picasso:2.4.0'
2625
compile 'com.loopj.android:android-async-http:1.4.6'
2726
compile 'com.jakewharton:butterknife:6.1.0'

app/src/main/AndroidManifest.xml

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@
99
android:targetSdkVersion="21" />
1010

1111
<uses-permission android:name="android.permission.INTERNET" />
12-
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
13-
14-
<android:uses-permission android:name="android.permission.READ_PHONE_STATE" />
15-
<android:uses-permission
16-
android:name="android.permission.READ_EXTERNAL_STORAGE"
17-
android:maxSdkVersion="18" />
12+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
13+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
14+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
15+
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
1816

1917
<application
2018
android:name=".TwitterApplication"
@@ -24,7 +22,7 @@
2422
android:theme="@style/AppTheme" >
2523
<meta-data
2624
android:name="AA_DB_NAME"
27-
android:value="RestClient.db" />
25+
android:value="MySimpleTweets.db" />
2826
<meta-data
2927
android:name="AA_DB_VERSION"
3028
android:value="1" />

app/src/main/java/com/codepath/apps/mysimpletweets/activities/LoginActivity.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class LoginActivity extends OAuthLoginActionBarActivity<TwitterClient> {
1818
protected void onCreate(Bundle savedInstanceState) {
1919
super.onCreate(savedInstanceState);
2020
setContentView(R.layout.activity_login);
21+
getSupportActionBar().hide();
2122
}
2223

2324

app/src/main/java/com/codepath/apps/mysimpletweets/activities/TimelineActivity.java

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import android.view.Menu;
1010
import android.view.MenuItem;
1111
import android.widget.ListView;
12+
import android.widget.ProgressBar;
1213
import android.widget.Toast;
1314

1415
import com.codepath.apps.mysimpletweets.R;
@@ -17,8 +18,10 @@
1718
import com.codepath.apps.mysimpletweets.adapters.EndlessScrollListener;
1819
import com.codepath.apps.mysimpletweets.adapters.TweetsArrayAdapter;
1920
import com.codepath.apps.mysimpletweets.fragments.TweetFragment;
21+
import com.codepath.apps.mysimpletweets.models.CacheManager;
2022
import com.codepath.apps.mysimpletweets.models.Tweet;
2123
import com.codepath.apps.mysimpletweets.models.User;
24+
import com.codepath.apps.mysimpletweets.utils.ConnectivityHelper;
2225
import com.loopj.android.http.JsonHttpResponseHandler;
2326

2427
import org.apache.http.Header;
@@ -39,6 +42,7 @@ public class TimelineActivity extends ActionBarActivity implements TweetFragment
3942
private TweetsArrayAdapter aTweets;
4043
@InjectView(R.id.lvTweets) ListView lvTweets;
4144
@InjectView(R.id.swipeContainer) SwipeRefreshLayout swipeContainer;
45+
@InjectView(R.id.pbLoading) ProgressBar progressBar;
4246

4347
private long max_id;
4448
private User currentUser;
@@ -61,9 +65,9 @@ public void onLoadMore(int page, int totalItemsCount) {
6165
aTweets = new TweetsArrayAdapter(this, tweets);
6266
lvTweets.setAdapter(aTweets);
6367
client = TwitterApplication.getRestClient();
64-
max_id = 0;
65-
aTweets.clear();
68+
aTweets.addAll(CacheManager.latestTweets());
6669
populateCurrentUser();
70+
max_id = 0;
6771
populateTimeline();
6872

6973
// Setup refresh listener which triggers new data loading
@@ -74,7 +78,6 @@ public void onRefresh() {
7478
// Make sure you call swipeContainer.setRefreshing(false)
7579
// once the network request has completed successfully.
7680
max_id = 0;
77-
aTweets.clear();
7881
populateCurrentUser();
7982
populateTimeline();
8083
}
@@ -91,7 +94,7 @@ private void populateCurrentUser() {
9194
@Override
9295
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
9396
currentUser = User.fromJSON(response);
94-
Log.d(TAG, "user populated: " + currentUser);
97+
Log.d(TAG, "user populated: " + currentUser.getScreenName());
9598
}
9699

97100
@Override
@@ -111,21 +114,35 @@ public void customLoadMoreDataFromApi(int offset) {
111114
}
112115

113116
private void populateTimeline() {
114-
client.getHomeTimeline(max_id, new JsonHttpResponseHandler() {
115-
@Override
116-
public void onSuccess(int statusCode, Header[] headers, JSONArray json) {
117-
aTweets.addAll(Tweet.fromJSON(json));
118-
Tweet lastTweet = aTweets.getItem(aTweets.getCount() - 1);
119-
max_id = lastTweet.getUid() - 1;
120-
// Log.d(TAG, "max_id = " + max_id);
121-
swipeContainer.setRefreshing(false);
122-
}
117+
if (!ConnectivityHelper.isNetworkAvailable(this)) {
118+
ConnectivityHelper.notifyUserAboutNoInternetConnectivity(this);
119+
} else {
120+
progressBar.setVisibility(ProgressBar.VISIBLE);
121+
client.getHomeTimeline(max_id, new JsonHttpResponseHandler() {
122+
@Override
123+
public void onSuccess(int statusCode, Header[] headers, JSONArray json) {
124+
if (max_id == 0) {
125+
aTweets.clear();
126+
}
127+
ArrayList<Tweet> responseTweets = Tweet.fromJSON(json);
128+
// Fire and forget
129+
CacheManager.saveTweets(responseTweets);
130+
aTweets.addAll(responseTweets);
131+
Tweet lastTweet = aTweets.getItem(aTweets.getCount() - 1);
132+
max_id = lastTweet.getUid() - 1;
133+
// Log.d(TAG, "max_id = " + max_id);
134+
swipeContainer.setRefreshing(false);
135+
progressBar.setVisibility(ProgressBar.INVISIBLE);
136+
}
123137

124-
@Override
125-
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
126-
Log.d(TAG, errorResponse.toString());
127-
}
128-
});
138+
@Override
139+
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
140+
Log.e(TAG, "Failed to call API: " + throwable);
141+
progressBar.setVisibility(ProgressBar.INVISIBLE);
142+
ConnectivityHelper.notifyUserAboutAPIError(TimelineActivity.this);
143+
}
144+
});
145+
}
129146
}
130147

131148

@@ -159,19 +176,27 @@ public void onTweet(String tweet) {
159176
tweet = tweet.trim();
160177
Log.d(TAG, "Posting tweet to Twitter: " + tweet);
161178
if (tweet.length() > 0){
162-
client.postTweet(tweet, new JsonHttpResponseHandler() {
163-
@Override
164-
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
165-
tweets.add(0, Tweet.fromJSON(response));
166-
aTweets.notifyDataSetChanged();
167-
Toast.makeText(TimelineActivity.this, R.string.tweet_posted_successfully, Toast.LENGTH_SHORT).show();
168-
}
169-
170-
@Override
171-
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
172-
Log.d(TAG, errorResponse.toString());
173-
}
174-
});
179+
if (!ConnectivityHelper.isNetworkAvailable(this)) {
180+
ConnectivityHelper.notifyUserAboutNoInternetConnectivity(this);
181+
} else {
182+
progressBar.setVisibility(ProgressBar.VISIBLE);
183+
client.postTweet(tweet, new JsonHttpResponseHandler() {
184+
@Override
185+
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
186+
tweets.add(0, Tweet.fromJSON(response));
187+
aTweets.notifyDataSetChanged();
188+
progressBar.setVisibility(ProgressBar.INVISIBLE);
189+
Toast.makeText(TimelineActivity.this, R.string.tweet_posted_successfully, Toast.LENGTH_SHORT).show();
190+
}
191+
192+
@Override
193+
public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
194+
Log.e(TAG, "Failed to call API: " + throwable);
195+
progressBar.setVisibility(ProgressBar.INVISIBLE);
196+
ConnectivityHelper.notifyUserAboutAPIError(TimelineActivity.this);
197+
}
198+
});
199+
}
175200
}
176201
}
177202
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.codepath.apps.mysimpletweets.models;
2+
3+
import com.activeandroid.ActiveAndroid;
4+
import com.activeandroid.query.Select;
5+
6+
import java.util.List;
7+
8+
public class CacheManager {
9+
10+
public static void saveTweets(List<Tweet> tweets) {
11+
ActiveAndroid.beginTransaction();
12+
try {
13+
for (Tweet tweet : tweets) {
14+
tweet.getUser().save();
15+
tweet.save();
16+
}
17+
ActiveAndroid.setTransactionSuccessful();
18+
} finally {
19+
ActiveAndroid.endTransaction();
20+
}
21+
}
22+
23+
public static List<Tweet> latestTweets() {
24+
return new Select().from(Tweet.class).orderBy("createdAt DESC").limit("250").execute();
25+
}
26+
}

app/src/main/java/com/codepath/apps/mysimpletweets/models/Tweet.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,51 @@
11
package com.codepath.apps.mysimpletweets.models;
22

3+
import com.activeandroid.Model;
4+
import com.activeandroid.annotation.Column;
5+
import com.activeandroid.annotation.Table;
6+
37
import org.json.JSONArray;
48
import org.json.JSONException;
59
import org.json.JSONObject;
610

711
import java.util.ArrayList;
812

9-
public class Tweet {
13+
@Table(name = "Tweets")
14+
public class Tweet extends Model{
1015

16+
@Column(name = "body")
1117
private String mBody;
18+
19+
// This is the unique id given by the server
20+
@Column(name = "remote_id", unique = true, onUniqueConflict = Column.ConflictAction.REPLACE)
1221
private long mUid;
22+
23+
@Column(name = "createdAt")
1324
private String mCreatedAt;
25+
26+
@Column(name = "User", onUpdate = Column.ForeignKeyAction.CASCADE, onDelete = Column.ForeignKeyAction.CASCADE)
1427
private User mUser;
28+
29+
@Column(name = "retweetCount")
1530
private int mRetweetCount;
31+
32+
@Column(name = "favoritesCount")
1633
private int mFavoritesCount;
1734

35+
// Make sure to have a default constructor for every ActiveAndroid model
36+
public Tweet() {
37+
super();
38+
}
39+
40+
public Tweet(String body, long uid, String createdAt, User user, int retweetCount, int favoritesCount) {
41+
mBody = body;
42+
mUid = uid;
43+
mCreatedAt = createdAt;
44+
mUser = user;
45+
mRetweetCount = retweetCount;
46+
mFavoritesCount = favoritesCount;
47+
}
48+
1849
public int getRetweetCount() {
1950
return mRetweetCount;
2051
}

app/src/main/java/com/codepath/apps/mysimpletweets/models/User.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,27 @@
33
import android.os.Parcel;
44
import android.os.Parcelable;
55

6+
import com.activeandroid.Model;
7+
import com.activeandroid.annotation.Column;
8+
import com.activeandroid.annotation.Table;
9+
610
import org.json.JSONException;
711
import org.json.JSONObject;
812

9-
public class User implements Parcelable {
13+
@Table(name = "Users")
14+
public class User extends Model implements Parcelable {
1015

16+
@Column(name = "name")
1117
private String mName;
18+
19+
// This is the unique id given by the server
20+
@Column(name = "remote_id", unique = true, onUniqueConflict = Column.ConflictAction.REPLACE)
1221
private long mUid;
22+
23+
@Column(name = "screenName")
1324
private String mScreenName;
25+
26+
@Column(name = "profileImageUrl")
1427
private String mProfileImageUrl;
1528

1629
public String getName() {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.codepath.apps.mysimpletweets.utils;
2+
3+
import android.app.AlertDialog;
4+
import android.content.Context;
5+
import android.content.DialogInterface;
6+
import android.net.ConnectivityManager;
7+
import android.net.NetworkInfo;
8+
9+
import com.codepath.apps.mysimpletweets.R;
10+
11+
public class ConnectivityHelper {
12+
13+
public static void notifyUserAboutNoInternetConnectivity(Context context) {
14+
notifyUserAboutGenericError(context, R.string.no_internet_connection_label);
15+
}
16+
17+
public static void notifyUserAboutAPIError(Context context) {
18+
notifyUserAboutGenericError(context, R.string.api_failure_label);
19+
}
20+
21+
public static void notifyUserAboutGenericError(Context context, int textId) {
22+
new AlertDialog.Builder(context).setTitle(textId)
23+
.setNeutralButton(R.string.ok_label,
24+
new DialogInterface.OnClickListener() {
25+
public void onClick(DialogInterface dialog, int which) {
26+
dialog.dismiss();
27+
}
28+
}).show();
29+
}
30+
31+
public static Boolean isNetworkAvailable(Context context) {
32+
ConnectivityManager connectivityManager
33+
= (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
34+
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
35+
return activeNetworkInfo != null && activeNetworkInfo.isConnectedOrConnecting();
36+
}
37+
}
2.76 KB
Loading
118 KB
Loading

0 commit comments

Comments
 (0)