diff options
Diffstat (limited to 'Android/06-Notifications/Notifications/src/course/labs/notificationslab')
6 files changed, 650 insertions, 0 deletions
diff --git a/Android/06-Notifications/Notifications/src/course/labs/notificationslab/DownloaderTask.java b/Android/06-Notifications/Notifications/src/course/labs/notificationslab/DownloaderTask.java new file mode 100644 index 0000000..b792b38 --- /dev/null +++ b/Android/06-Notifications/Notifications/src/course/labs/notificationslab/DownloaderTask.java @@ -0,0 +1,217 @@ +package course.labs.notificationslab; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.URL; + +import android.app.Activity; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import android.util.Log; +import android.widget.RemoteViews; + +public class DownloaderTask extends AsyncTask<String, Void, String[]> { + + private static final int SIM_NETWORK_DELAY = 5000; + private static final String TAG = "Lab-Notifications"; + private final int MY_NOTIFICATION_ID = 11151990; + private String mFeeds[] = new String[3]; + private MainActivity mParentActivity; + private Context mApplicationContext; + + // Change this variable to false if you do not have a stable network + // connection + private static final boolean HAS_NETWORK_CONNECTION = true; + + // Raw feed file IDs used if you do not have a stable connection + public static final int txtFeeds[] = { R.raw.tswift, R.raw.rblack, R.raw.lgaga }; + + // Constructor + public DownloaderTask(MainActivity parentActivity) { + super(); + mParentActivity = parentActivity; + mApplicationContext = parentActivity.getApplicationContext(); + } + + @Override + protected String[] doInBackground(String... urlParameters) { + log("Entered doInBackground()"); + return download(urlParameters); + } + + private String[] download(String urlParameters[]) { + + boolean downloadCompleted = false; + + try { + + for (int idx = 0; idx < urlParameters.length; idx++) { + + URL url = new URL(urlParameters[idx]); + try { + Thread.sleep(SIM_NETWORK_DELAY); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + InputStream inputStream; + BufferedReader in; + + // Alternative for students without + // a network connection + if (HAS_NETWORK_CONNECTION) { + inputStream = url.openStream(); + in = new BufferedReader(new InputStreamReader(inputStream)); + } else { + inputStream = mApplicationContext.getResources().openRawResource(txtFeeds[idx]); + in = new BufferedReader(new InputStreamReader(inputStream)); + } + + String readLine; + StringBuffer buf = new StringBuffer(); + + while ((readLine = in.readLine()) != null) { + buf.append(readLine); + } + + mFeeds[idx] = buf.toString(); + + if (null != in) { + in.close(); + } + } + + downloadCompleted = true; + + } catch (IOException e) { + e.printStackTrace(); + } + + log("Tweet Download Completed:" + downloadCompleted); + + notify(downloadCompleted); + + return mFeeds; + } + + // Call back to the MainActivity to update the feed display + @Override + protected void onPostExecute(String[] result) { + super.onPostExecute(result); + + if (mParentActivity != null) { + mParentActivity.setRefreshed(result); + } + + } + + // If necessary, notifies the user that the tweet downloads are complete. + // Sends an ordered broadcast back to the BroadcastReceiver in MainActivity + // to determine whether the notification is necessary. + private void notify(final boolean success) { + log("Entered notify()"); + final Intent restartMainActivtyIntent = new Intent(mApplicationContext, MainActivity.class); + restartMainActivtyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + if (success) { + // Save tweets to a file + saveTweetsToFile(); + } + + // Sends an ordered broadcast to determine whether MainActivity is + // active and in the foreground. Creates a new BroadcastReceiver + // to receive a result indicating the state of MainActivity + + // The Action for this broadcast Intent is MainActivity.DATA_REFRESHED_ACTION + // The result Activity.RESULT_OK, indicates that MainActivity is active and + // in the foreground. + mApplicationContext.sendOrderedBroadcast( + new Intent(MainActivity.DATA_REFRESHED_ACTION), null, new BroadcastReceiver() { + + final String failMsg = "Download has failed. Please retry Later."; + final String successMsg = "Download completed successfully."; + + @Override + public void onReceive(Context context, Intent intent) { + + log("Entered result receiver's onReceive() method"); + + // TODO: Check whether the result code is RESULT_OK + + if (/*change this*/ true) { + + // TODO: If so, create a PendingIntent using the + // restartMainActivityIntent and set its flags + // to FLAG_UPDATE_CURRENT + + final PendingIntent pendingIntent = null; + + // Uses R.layout.custom_notification for the + // layout of the notification View. The xml + // file is in res/layout/custom_notification.xml + RemoteViews mContentView = new RemoteViews( + mApplicationContext.getPackageName(), + R.layout.custom_notification); + + // TODO: Set the notification View's text to + // reflect whether or the download completed + // successfully + + // TODO: Use the Notification.Builder class to + // create the Notification. You will have to set + // several pieces of information. You can use + // android.R.drawable.stat_sys_warning + // for the small icon. You should also setAutoCancel(true). + + Notification.Builder notificationBuilder = null; + + // TODO: Send the notification + + log("Notification Area Notification sent"); + } + } + }, null, 0, null, null); + } + + // Saves the tweets to a file + private void saveTweetsToFile() { + PrintWriter writer = null; + try { + FileOutputStream fos = mApplicationContext.openFileOutput( + MainActivity.TWEET_FILENAME, Context.MODE_PRIVATE); + writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter( + fos))); + + for (String s : mFeeds) { + writer.println(s); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (null != writer) { + writer.close(); + } + } + } + + // Simplified log output method + private void log(String msg) { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Log.i(TAG, msg); + } +} diff --git a/Android/06-Notifications/Notifications/src/course/labs/notificationslab/FeedFragment.java b/Android/06-Notifications/Notifications/src/course/labs/notificationslab/FeedFragment.java new file mode 100644 index 0000000..549524a --- /dev/null +++ b/Android/06-Notifications/Notifications/src/course/labs/notificationslab/FeedFragment.java @@ -0,0 +1,35 @@ +package course.labs.notificationslab; + +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public class FeedFragment extends Fragment { + + private TextView mTextView; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.feed, container, false); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mTextView = (TextView) getView().findViewById(R.id.feed_view); + } + + void update(String feed) { + if (null != mTextView) { + mTextView.setText(feed); + } + } +} diff --git a/Android/06-Notifications/Notifications/src/course/labs/notificationslab/FriendsFragment.java b/Android/06-Notifications/Notifications/src/course/labs/notificationslab/FriendsFragment.java new file mode 100644 index 0000000..b90f8d3 --- /dev/null +++ b/Android/06-Notifications/Notifications/src/course/labs/notificationslab/FriendsFragment.java @@ -0,0 +1,44 @@ +package course.labs.notificationslab; + +import android.app.Activity; +import android.app.ListFragment; +import android.os.Bundle; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +public class FriendsFragment extends ListFragment { + + private SelectionListener mCallback; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, MainActivity.FRIENDS)); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + // This makes sure that the container activity has implemented + // the callback interface. If not, it throws an exception + try { + mCallback = (SelectionListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + " must implement SelectionListener"); + } + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); + } + + @Override + public void onListItemClick(ListView l, View view, int position, long id) { + // Send the event to the host activity + mCallback.onItemSelected(position); + } +} diff --git a/Android/06-Notifications/Notifications/src/course/labs/notificationslab/MainActivity.java b/Android/06-Notifications/Notifications/src/course/labs/notificationslab/MainActivity.java new file mode 100644 index 0000000..eebf924 --- /dev/null +++ b/Android/06-Notifications/Notifications/src/course/labs/notificationslab/MainActivity.java @@ -0,0 +1,235 @@ +package course.labs.notificationslab; + +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.app.Activity; +import android.app.FragmentManager; +import android.app.FragmentTransaction; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.util.Log; + +public class MainActivity extends Activity implements SelectionListener { + + public static final String TWEET_FILENAME = "tweets.txt"; + public static final String[] FRIENDS = { "taylorswift13", "msrebeccablack", "ladygaga" }; + public static final String DATA_REFRESHED_ACTION = "course.labs.notificationslab.DATA_REFRESHED"; + + private static final int NUM_FRIENDS = 3; + private static final String URL_LGAGA = "https://d396qusza40orc.cloudfront.net/android%2FLabs%2FUserNotifications%2Fladygaga.txt"; + private static final String URL_RBLACK = "https://d396qusza40orc.cloudfront.net/android%2FLabs%2FUserNotifications%2Frebeccablack.txt"; + private static final String URL_TSWIFT = "https://d396qusza40orc.cloudfront.net/android%2FLabs%2FUserNotifications%2Ftaylorswift.txt"; + private static final String TAG = "Lab-Notifications"; + private static final long TWO_MIN = 2 * 60 * 1000; + private static final int UNSELECTED = -1; + + private FragmentManager mFragmentManager; + private FriendsFragment mFriendsFragment; + private boolean mIsFresh; + private BroadcastReceiver mRefreshReceiver; + private int mFeedSelected = UNSELECTED; + private FeedFragment mFeedFragment; + private String[] mRawFeeds = new String[3]; + private String[] mProcessedFeeds = new String[3]; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + mFragmentManager = getFragmentManager(); + addFriendsFragment(); + // The feed is fresh if it was downloaded less than 2 minutes ago + mIsFresh = (System.currentTimeMillis() - getFileStreamPath( TWEET_FILENAME).lastModified()) < TWO_MIN; + ensureData(); + } + + // Add Friends Fragment to Activity + private void addFriendsFragment() { + mFriendsFragment = new FriendsFragment(); + mFriendsFragment.setArguments(getIntent().getExtras()); + FragmentTransaction transaction = mFragmentManager.beginTransaction(); + transaction.add(R.id.fragment_container, mFriendsFragment); + transaction.commit(); + } + + // If stored Tweets are not fresh, reload them from network + // Otherwise, load them from file + private void ensureData() { + + log("In ensureData(), mIsFresh:" + mIsFresh); + + if (!mIsFresh) { + // TODO: + // Show a Toast Notification to inform user that + // the app is "Downloading Tweets from Network" + log ("Issuing Toast Message"); + + // TODO: + // Start new AsyncTask to download Tweets from network + + // Set up a BroadcastReceiver to receive an Intent when download + // finishes. + mRefreshReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + log("BroadcastIntent received in MainActivity"); + // TODO: + // Check to make sure this is an ordered broadcast + // Let sender know that the Intent was received + // by setting result code to RESULT_OK + } + }; + } else { + loadTweetsFromFile(); + parseJSON(); + updateFeed(); + } + } + + // Called when new Tweets have been downloaded + public void setRefreshed(String[] feeds) { + mRawFeeds[0] = feeds[0]; + mRawFeeds[1] = feeds[1]; + mRawFeeds[2] = feeds[2]; + + parseJSON(); + updateFeed(); + mIsFresh = true; + }; + + // Called when a Friend is clicked on + @Override + public void onItemSelected(int position) { + mFeedSelected = position; + mFeedFragment = addFeedFragment(); + + if (mIsFresh) { + updateFeed(); + } + } + + // Calls FeedFragement.update, passing in the + // the tweets for the currently selected friend + void updateFeed() { + if (null != mFeedFragment) + mFeedFragment.update(mProcessedFeeds[mFeedSelected]); + } + + // Add FeedFragment to Activity + private FeedFragment addFeedFragment() { + FeedFragment feedFragment; + feedFragment = new FeedFragment(); + + FragmentTransaction transaction = mFragmentManager.beginTransaction(); + + transaction.replace(R.id.fragment_container, feedFragment); + transaction.addToBackStack(null); + + transaction.commit(); + mFragmentManager.executePendingTransactions(); + return feedFragment; + } + + // Register the BroadcastReceiver + @Override + protected void onResume() { + super.onResume(); + // TODO: + // Register the BroadcastReceiver to receive a + // DATA_REFRESHED_ACTION broadcast + } + + @Override + protected void onPause() { + // TODO: + // Unregister the BroadcastReceiver + super.onPause(); + } + + // Convert raw Tweet data (in JSON format) into text for display + public void parseJSON() { + + JSONArray[] JSONFeeds = new JSONArray[NUM_FRIENDS]; + + for (int i = 0; i < NUM_FRIENDS; i++) { + try { + JSONFeeds[i] = new JSONArray(mRawFeeds[i]); + } catch (JSONException e) { + e.printStackTrace(); + } + + String name = ""; + String tweet = ""; + + JSONArray tmp = JSONFeeds[i]; + + // string buffer for twitter feeds + StringBuffer tweetRec = new StringBuffer(""); + + for (int j = 0; j < tmp.length(); j++) { + try { + tweet = tmp.getJSONObject(j).getString("text"); + JSONObject user = (JSONObject) tmp.getJSONObject(j).get( + "user"); + name = user.getString("name"); + + } catch (JSONException e) { + e.printStackTrace(); + } + + tweetRec.append(name + " - " + tweet + "\n\n"); + } + mProcessedFeeds[i] = tweetRec.toString(); + } + } + + // Retrieve feeds text from a file + // Store them in mRawTextFeed[] + private void loadTweetsFromFile() { + BufferedReader reader = null; + + try { + FileInputStream fis = openFileInput(TWEET_FILENAME); + reader = new BufferedReader(new InputStreamReader(fis)); + String s = null; + int i = 0; + while (null != (s = reader.readLine()) && i < NUM_FRIENDS) { + mRawFeeds[i] = s; + i++; + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (null != reader) { + try { + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + // Simplified log output method + private void log(String msg) { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Log.i(TAG, msg); + } +} diff --git a/Android/06-Notifications/Notifications/src/course/labs/notificationslab/SelectionListener.java b/Android/06-Notifications/Notifications/src/course/labs/notificationslab/SelectionListener.java new file mode 100644 index 0000000..87c9dbe --- /dev/null +++ b/Android/06-Notifications/Notifications/src/course/labs/notificationslab/SelectionListener.java @@ -0,0 +1,5 @@ +package course.labs.notificationslab; + +public interface SelectionListener { + public void onItemSelected(int position); +} diff --git a/Android/06-Notifications/Notifications/src/course/labs/notificationslab/TestFrontEndActivity.java b/Android/06-Notifications/Notifications/src/course/labs/notificationslab/TestFrontEndActivity.java new file mode 100644 index 0000000..72b1a6f --- /dev/null +++ b/Android/06-Notifications/Notifications/src/course/labs/notificationslab/TestFrontEndActivity.java @@ -0,0 +1,114 @@ +package course.labs.notificationslab; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.os.Bundle; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; + +public class TestFrontEndActivity extends Activity { + + private final static long DAWN_OF_TIME = 0; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_test_front_end); + + Button ageTweetsButton = (Button) findViewById(R.id.age_tweets_button); + ageTweetsButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + setFileAge(DAWN_OF_TIME); + } + }); + + Button rejuvTweetsButton = (Button) findViewById(R.id.rejuv_tweets_button); + rejuvTweetsButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + setFileAge(System.currentTimeMillis()); + } + }); + + Button startMainActivityButton = (Button) findViewById(R.id.start_main_button); + startMainActivityButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + startActivity(new Intent(TestFrontEndActivity.this, + MainActivity.class)); + } + }); + createTweetFileIfMissing(); + } + + private void createTweetFileIfMissing() { + + String fname = TestFrontEndActivity.this.getFilesDir() + "/" + MainActivity.TWEET_FILENAME; + + File file = new File(fname); + if (!file.exists()) { + + PrintWriter out = null; + BufferedReader in = null; + + try { + out = new PrintWriter(new BufferedWriter( + new OutputStreamWriter(openFileOutput( + MainActivity.TWEET_FILENAME, + Context.MODE_PRIVATE)))); + + for (int resId : DownloaderTask.txtFeeds) { + in = new BufferedReader(new InputStreamReader( + getResources().openRawResource(resId))); + + String line; + StringBuffer buffer = new StringBuffer(); + + while ((line = in.readLine()) != null) { + buffer.append(line); + } + out.println(buffer); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (Resources.NotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (null != in) { + in.close(); + } + if (null != out) { + out.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + private void setFileAge(long timestamp) { + String fname = TestFrontEndActivity.this.getFilesDir() + "/" + MainActivity.TWEET_FILENAME; + File file = new File(fname); + if (file.exists()) { + file.setLastModified(timestamp); + } + } + +} |