├── .DS_Store
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── TwitterDemoGIF.gif
├── _config.yml
├── app
├── .DS_Store
├── build.gradle
└── src
│ ├── .DS_Store
│ └── main
│ ├── .DS_Store
│ ├── AndroidManifest.xml
│ ├── ic_compose-web.png
│ ├── ic_face_placeholder-web.png
│ ├── ic_launcher-web.png
│ ├── java
│ └── com
│ │ └── codepath
│ │ └── apps
│ │ └── restclienttemplate
│ │ ├── ComposeActivity.java
│ │ ├── LoginActivity.java
│ │ ├── MyDatabase.java
│ │ ├── TimelineActivity.java
│ │ ├── TweetAdapter.java
│ │ ├── TweetDetailsActivity.java
│ │ ├── TwitterApp.java
│ │ ├── TwitterClient.java
│ │ ├── UserProfileActivity.java
│ │ ├── fragments
│ │ ├── HomeTimelineFragment.java
│ │ ├── MentionsTimelineFragment.java
│ │ ├── TweetsListFragment.java
│ │ ├── TweetsPagerAdapter.java
│ │ └── UserTimelineFragment.java
│ │ └── models
│ │ ├── Media.java
│ │ ├── SampleModel.java
│ │ ├── Tweet.java
│ │ ├── TweetText.java
│ │ └── User.java
│ └── res
│ ├── .DS_Store
│ ├── drawable-hdpi
│ ├── .DS_Store
│ ├── ic_face_black_24dp.png
│ ├── ic_face_placeholder.png
│ ├── ic_heart_clear_light_blue.png
│ ├── ic_heart_clear_red.png
│ ├── ic_heart_solid_light_blue.png
│ ├── ic_launcher.png
│ ├── ic_mode_edit_black_48dp.png
│ ├── ic_person_v1.png
│ ├── ic_person_v2.png
│ ├── ic_person_v3.png
│ ├── ic_picture_placeholder.png
│ ├── ic_reply.png
│ ├── ic_reply_accent.png
│ ├── ic_reply_black_48dp.png
│ ├── ic_retweet.png
│ ├── ic_retweet_green.png
│ ├── ic_tweet.png
│ ├── ic_tweet_accent.png
│ ├── ic_tweet_quil.png
│ └── ic_tweet_quil_accent.png
│ ├── drawable-mdpi
│ ├── ic_face_black_24dp.png
│ ├── ic_face_placeholder.png
│ ├── ic_heart_clear_light_blue.png
│ ├── ic_heart_clear_red.png
│ ├── ic_heart_solid_light_blue.png
│ ├── ic_launcher.png
│ ├── ic_mode_edit_black_48dp.png
│ ├── ic_person_v1.png
│ ├── ic_person_v2.png
│ ├── ic_person_v3.png
│ ├── ic_picture_placeholder.png
│ ├── ic_reply.png
│ ├── ic_reply_accent.png
│ ├── ic_reply_black_48dp.png
│ ├── ic_retweet.png
│ ├── ic_retweet_green.png
│ ├── ic_tweet.png
│ ├── ic_tweet_accent.png
│ ├── ic_tweet_quil.png
│ └── ic_tweet_quil_accent.png
│ ├── drawable-xhdpi
│ ├── ic_face_black_24dp.png
│ ├── ic_face_placeholder.png
│ ├── ic_heart_clear_light_blue.png
│ ├── ic_heart_clear_red.png
│ ├── ic_heart_solid_light_blue.png
│ ├── ic_launcher.png
│ ├── ic_mode_edit_black_48dp.png
│ ├── ic_person_v1.png
│ ├── ic_person_v2.png
│ ├── ic_person_v3.png
│ ├── ic_picture_placeholder.png
│ ├── ic_reply.png
│ ├── ic_reply_accent.png
│ ├── ic_reply_black_48dp.png
│ ├── ic_retweet.png
│ ├── ic_retweet_green.png
│ ├── ic_tweet.png
│ ├── ic_tweet_accent.png
│ ├── ic_tweet_quil.png
│ └── ic_tweet_quil_accent.png
│ ├── drawable-xxhdpi
│ ├── ic_face_black_24dp.png
│ ├── ic_face_placeholder.png
│ ├── ic_heart_clear_light_blue.png
│ ├── ic_heart_clear_red.png
│ ├── ic_heart_solid_light_blue.png
│ ├── ic_launcher.png
│ ├── ic_mode_edit_black_48dp.png
│ ├── ic_person_v1.png
│ ├── ic_person_v2.png
│ ├── ic_person_v3.png
│ ├── ic_picture_placeholder.png
│ ├── ic_reply.png
│ ├── ic_reply_accent.png
│ ├── ic_reply_black_48dp.png
│ ├── ic_retweet.png
│ ├── ic_retweet_green.png
│ ├── ic_tweet.png
│ ├── ic_tweet_accent.png
│ ├── ic_tweet_quil.png
│ └── ic_tweet_quil_accent.png
│ ├── drawable-xxxhdpi
│ ├── ic_face_black_24dp.png
│ ├── ic_face_placeholder.png
│ ├── ic_heart_clear_light_blue.png
│ ├── ic_heart_clear_red.png
│ ├── ic_heart_solid_light_blue.png
│ ├── ic_mode_edit_black_48dp.png
│ ├── ic_person_v1.png
│ ├── ic_person_v2.png
│ ├── ic_person_v3.png
│ ├── ic_picture_placeholder.png
│ ├── ic_reply.png
│ ├── ic_reply_accent.png
│ ├── ic_reply_black_48dp.png
│ ├── ic_retweet.png
│ ├── ic_retweet_green.png
│ ├── ic_tweet.png
│ ├── ic_tweet_accent.png
│ ├── ic_tweet_quil.png
│ └── ic_tweet_quil_accent.png
│ ├── drawable
│ ├── .DS_Store
│ ├── button_follow.xml
│ ├── button_unfollow.xml
│ ├── ic_back_button_accent.xml
│ ├── ic_back_button_light_blue.xml
│ ├── ic_close_accent.xml
│ ├── ic_heart_clear_light_blue_svg.xml
│ ├── ic_heart_clear_red_svg.xml
│ ├── ic_heart_solid_light_blue_svg.xml
│ ├── ic_heart_solid_red_svg.xml
│ ├── ic_person_v1_svg.xml
│ ├── ic_person_v2_svg.xml
│ ├── ic_person_v3_svg.xml
│ ├── ic_picture_placeholder_svg.xml
│ ├── ic_reply_accent_svg.xml
│ ├── ic_reply_svg.xml
│ ├── ic_retweet_green_svg.xml
│ ├── ic_retweet_svg.xml
│ ├── ic_tweet_accent_svg.xml
│ ├── ic_tweet_quil_accent_svg.xml
│ ├── ic_tweet_quil_svg.xml
│ ├── ic_tweet_svg.xml
│ └── rounded_corners.xml
│ ├── layout
│ ├── activity_compose.xml
│ ├── activity_login.xml
│ ├── activity_timeline.xml
│ ├── activity_tweet_details.xml
│ ├── activity_user_profile.xml
│ ├── fragments_tweets_list.xml
│ └── item_tweet.xml
│ ├── menu
│ ├── login.xml
│ └── menu_timeline.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── values-sw600dp
│ └── dimens.xml
│ ├── values-sw720dp-land
│ └── dimens.xml
│ ├── values-v14
│ └── styles.xml
│ └── values
│ ├── .DS_Store
│ ├── colors.xml
│ ├── dimens.xml
│ ├── secrets.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle
├── lint.xml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── success.md
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Source: https://gist.github.com/nesquena/5617544/raw/53710b374e7df3302df43b552488d876040ada3d/.gitignore
2 |
3 | # built application files
4 | *.apk
5 | *.ap_
6 |
7 | # files for the dex VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # generated files
14 | bin/
15 | gen/
16 |
17 | # Local configuration file (sdk path, etc)
18 | local.properties
19 |
20 | # Eclipse project files
21 | .classpath
22 | .project
23 |
24 | # Proguard folder generated by Eclipse
25 | proguard/
26 |
27 | # Intellij project files
28 | *.iml
29 | *.ipr
30 | *.iws
31 | .idea/
32 |
33 | *.pydevproject
34 | .project
35 | .metadata
36 | bin/**
37 | tmp/**
38 | tmp/**/*
39 | *.tmp
40 | *.bak
41 | *.swp
42 | *~.nib
43 | local.properties
44 | .classpath
45 | .settings/
46 | .loadpath
47 |
48 | # External tool builders
49 | .externalToolBuilders/
50 |
51 | # Locally stored "Eclipse launch configurations"
52 | *.launch
53 |
54 | # CDT-specific
55 | .cproject
56 |
57 | # PDT-specific
58 | .buildpath
59 |
60 | # Android Studio project files
61 | *.iml
62 | .gradle
63 | .idea
64 | build
65 | import-summary.txt
66 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | jdk:
3 | - oraclejdk8
4 | sudo: false
5 | android:
6 | components:
7 | - tools
8 | - platform-tools
9 | - build-tools-25.0.3
10 | - android-25
11 | licenses:
12 | - android-sdk-license-.+
13 | script:
14 | - "./gradlew build check --daemon"
15 | after_failure: "cat $TRAVIS_BUILD_DIR/app/build/outputs/lint-results-debug.xml"
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 CodePath
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Project 3 - *Twitter*
2 |
3 | **Twitter** is an android app that allows a user to view his Twitter timeline and post a new tweet. The app utilizes [Twitter REST API](https://dev.twitter.com/rest/public).
4 | This app was made for learning purposes.
5 |
6 |
7 | Time spent: **35** hours spent in total (for two weeks)
8 |
9 | Elements used:
10 | - Icons from Material.io/icons/
11 |
12 | ## User Stories
13 |
14 | The following **required** functionality is completed:
15 |
16 | * [x] User can **sign in to Twitter** using OAuth login
17 | * [x] User can **view tweets from their home timeline**
18 | * [x] User is displayed the username, name, and body for each tweet
19 | * [x] User is displayed the [relative timestamp](https://gist.github.com/nesquena/f786232f5ef72f6e10a7) for each tweet "8m", "7h"
20 | * [x] User can **compose and post a new tweet**
21 | * [x] User can click a “Compose” icon in the Action Bar on the top right
22 | * [x] I actually used a floating action button isntead
23 | * [x] User can then enter a new tweet and post this to twitter
24 | * [x] User is taken back to home timeline with **new tweet visible** in timeline
25 | * [x] Newly created tweet should be manually inserted into the timeline and not rely on a full refresh
26 | * [x] User can switch between Timeline and Mention views using tabs.
27 | * [x] User can view their home timeline tweets.
28 | * [x] User can view the recent mentions of their username.
29 | * [x] User can compose tweets. See this conceptual guide for passing data into a timeline fragment.
30 | * [x] User can navigate to view their own profile.
31 | * [x] I actually placed the user click on the left of the screen.
32 | * [x] User can see picture, tagline, # of followers, # of following, and tweets on their profile.
33 | * [x] The users/verify_credentials endpoint can be used to access this information.
34 | * [x] User can click on the profile image in any tweet to see another user's profile.
35 | * [x] User can see picture, tagline, # of followers, # of following, and tweets of clicked user.
36 | * [x] Profile view should include that user's timeline.
37 | * [x] The users/show endpoint can be used to access this information.
38 |
39 | The following **optional** features are implemented:
40 |
41 | * [x] User can **see a counter with total number of characters left for tweet** on compose tweet page
42 | * [x] User can **pull down to refresh tweets timeline**
43 | * [x] User is using **"Twitter branded" colors and styles**
44 | * [x] Used the quite recent twitter theme *Night Mode*, which is why my app is dark blue.
45 | * [x] I also used a different set of icons that were nicer in my opinion.
46 | * [ ] User sees an **indeterminate progress indicator** when any background or network task is happening
47 | * [x] User can **select "reply" from detail view to respond to a tweet**
48 | * [x] User that wrote the original tweet is **automatically "@" replied in compose**
49 | * [x] User can tap a tweet to **open a detailed tweet view**
50 | * [x] User can **take favorite (and unfavorite) or reweet** actions on a tweet
51 | * [x] User can retweet/unretweet in detailed tweet mode.
52 | * [x] User can **see embedded image media within a tweet** on list or detail view.
53 | * [x] User can **click a link within a tweet body** on tweet details view. The click will launch the web browser with relevant page opened.
54 |
55 | The following **bonus** features are implemented:
56 |
57 | * [x] User can view more tweets as they scroll with infinite pagination
58 | * [ ] Compose tweet functionality is build using modal overlay
59 | * [x] Use Parcelable instead of Serializable using the popular [Parceler library](http://guides.codepath.com/android/Using-Parceler).
60 | * [x] Replace all icon drawables and other static image assets with [vector drawables](http://guides.codepath.com/android/Drawables#vector-drawables) where appropriate.
61 | * [ ] User can view following / followers list through any profile they view.
62 | * [x] User can see embedded image media within the tweet detail view
63 | * [ ] Use the popular ButterKnife annotation library to reduce view boilerplate.
64 | * [ ] On the Twitter timeline, leverage the [CoordinatorLayout](http://guides.codepath.com/android/Handling-Scrolls-with-CoordinatorLayout#responding-to-scroll-events) to apply scrolling behavior that [hides / shows the toolbar](http://guides.codepath.com/android/Using-the-App-ToolBar#reacting-to-scroll).
65 | * [ ] User can **open the twitter app offline and see last loaded tweets**. Persisted in SQLite tweets are refreshed on every application launch. While "live data" is displayed when app can get it from Twitter API, it is also saved for use in offline mode.
66 |
67 |
68 | The following **additional** features are implemented:
69 |
70 | * [ ] List anything else that you can get done to improve the app functionality!
71 | * [x] Set up to retrieve tweet counts and like counts
72 | * [x] Display activity text instead of AppName ("TWITTER"). e.g.: Home, New Tweet / In Reply To , Tweet (for detailed tweet)
73 | * [x] Improve the quality of the images received
74 | * [x] Remove Toast Statements on onClick events from TweetAdapter
75 | * [x] On reply, set handler to tweet id (in reply to, check twitter API) so that replies work in actual replies
76 | * [x] For images of tweets, have a different placeholder than the one of profile photos (similar to Flixster app placeholder)
77 | * [ ] Make infinite pagination on user profile's tweets
78 | * [x] Make the follow button actually work
79 | * [x] Add tabs to user profile
80 | * [x] User can see a user's profile by clicking on the profile picture of the user.
81 | * [ ] (!!!) Fix bugs resulting from infinite scroll and repeating the same tweets over and over when web error occurs
82 | * [ ] Figure out why maxid does not update
83 | * [x] Add a backpress button when user enters reply to a tweet (instead of letting be for the regular android backPress)
84 | * [ ] When click on backPress in the timeline, it just restart the application instead of quitting it. Fixing this bug would be a goal
85 | * [x] Make the follow button work on user profiles
86 | * [ ] On the profile view, display an action bar as the user scrolls down (with a back button)
87 | * [ ] Add search tab with various search functions implemented (mentions, tags, users, etc)
88 | * [ ] Fix the indeterminate progress bar
89 | * [ ] Add an icon for clicking on the profile (if possible, user an image instead of icon)
90 |
91 | ## Video Walkthrough
92 |
93 | Here's a walkthrough of implemented user stories:
94 |
95 | 
96 |
97 | GIF created with [LiceCap](http://www.cockos.com/licecap/).
98 |
99 | ## Notes
100 |
101 | Describe any challenges encountered while building the app.
102 |
103 | I was done with the requirements early-ish (although a bit later than most of my table). It was quite challenging to figure out how to take control of the UI in this app (spread things equally, load better quality images, add line separators for each tweet, etc). However, I was able to get it done up to how I wanted it to be, so I am happy about that. In addition, whenever I tried to implement something that I haven't implemented, it was difficult to figure out how to do it and having to search through at least 6 StackOverflow and Android Documentations pages altogether for each implementation. However, I feel like I got better at that. Finally, I really enjoyed taking more advantage of the android studio app: e.g.: using the debugger to find where the code breaks, using the monitor to check the log statements with TAGs, and using shortcuts to increase my efficiency/delivery.
104 |
105 | I really enjoyed building this app. I learned a lot and am more confident about my skill in android development.
106 |
107 | ## Open-source libraries used
108 |
109 | - [Android Async HTTP](https://github.com/loopj/android-async-http) - Simple asynchronous HTTP requests with JSON parsing
110 | - [Glide](https://github.com/bumptech/glide) - Image loading and caching library for Android
111 |
112 | ## License
113 |
114 | Copyright [2017] [Robert M. Vunabandi]
115 |
116 | Licensed under the Apache License, Version 2.0 (the "License");
117 | you may not use this file except in compliance with the License.
118 | You may obtain a copy of the License at
119 |
120 | http://www.apache.org/licenses/LICENSE-2.0
121 |
122 | Unless required by applicable law or agreed to in writing, software
123 | distributed under the License is distributed on an "AS IS" BASIS,
124 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
125 | See the License for the specific language governing permissions and
126 | limitations under the License.
127 |
128 |
--------------------------------------------------------------------------------
/TwitterDemoGIF.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/TwitterDemoGIF.gif
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-minimal
--------------------------------------------------------------------------------
/app/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/.DS_Store
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.3"
6 |
7 | defaultConfig {
8 | applicationId "com.codepath.apps.restclienttemplate"
9 | minSdkVersion 17
10 | targetSdkVersion 25
11 | vectorDrawables.useSupportLibrary = true
12 | }
13 |
14 | // Related to https://github.com/scribejava/scribejava/issues/480
15 | // Scribe expects Java 7 or this custom Apache library
16 | lintOptions {
17 | lintConfig rootProject.file('gradle/lint.xml')
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
24 | }
25 | }
26 | }
27 |
28 | repositories {
29 | jcenter()
30 | maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
31 | }
32 |
33 | def dbflow_version = "4.0.3"
34 |
35 | dependencies {
36 | compile fileTree(dir: 'libs', include: '*.jar')
37 | // Glide for remote image loading
38 | // Android Async Http for sending async network requests
39 | // DBFlow for simple persistence with an ORM
40 | annotationProcessor "com.github.Raizlabs.DBFlow:dbflow-processor:${dbflow_version}"
41 | compile "com.github.Raizlabs.DBFlow:dbflow-core:${dbflow_version}"
42 | compile "com.github.Raizlabs.DBFlow:dbflow:${dbflow_version}"
43 | compile 'com.github.bumptech.glide:glide:3.8.0'
44 | compile 'com.github.bumptech.glide:glide:3.8.0'
45 | compile 'com.codepath.libraries:android-oauth-handler:1.2.5'
46 | compile 'com.android.support:appcompat-v7:25.3.1'
47 | compile 'com.loopj.android:android-async-http:1.4.9'
48 | compile 'com.android.support.constraint:constraint-layout:1.0.2'
49 | compile 'com.android.support:recyclerview-v7:25.3.1'
50 | compile 'jp.wasabeef:glide-transformations:2.0.2'
51 | compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.4.1'
52 | compile 'org.parceler:parceler-api:1.1.6'
53 | compile 'com.android.support:design:25.3.1'
54 | compile 'com.jakewharton:butterknife:8.4.0'
55 | annotationProcessor 'org.parceler:parceler:1.1.6'
56 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
57 | compile "com.android.support:design:25.3.1"
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/.DS_Store
--------------------------------------------------------------------------------
/app/src/main/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/.DS_Store
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
16 |
17 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
33 |
34 |
35 |
36 |
37 |
40 |
41 |
42 |
46 |
47 |
48 |
51 |
52 |
53 |
56 |
57 |
--------------------------------------------------------------------------------
/app/src/main/ic_compose-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/ic_compose-web.png
--------------------------------------------------------------------------------
/app/src/main/ic_face_placeholder-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/ic_face_placeholder-web.png
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/ComposeActivity.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v4.content.ContextCompat;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.support.v7.widget.Toolbar;
8 | import android.text.Editable;
9 | import android.text.TextWatcher;
10 | import android.util.Log;
11 | import android.view.View;
12 | import android.widget.Button;
13 | import android.widget.EditText;
14 | import android.widget.ImageView;
15 | import android.widget.ProgressBar;
16 | import android.widget.TextView;
17 | import android.widget.Toast;
18 |
19 | import com.bumptech.glide.Glide;
20 | import com.codepath.apps.restclienttemplate.models.Tweet;
21 | import com.codepath.apps.restclienttemplate.models.User;
22 | import com.loopj.android.http.JsonHttpResponseHandler;
23 |
24 | import org.json.JSONArray;
25 | import org.json.JSONException;
26 | import org.json.JSONObject;
27 | import org.parceler.Parcels;
28 |
29 | import cz.msebera.android.httpclient.Header;
30 | import jp.wasabeef.glide.transformations.RoundedCornersTransformation;
31 |
32 | public class ComposeActivity extends AppCompatActivity {
33 | private TwitterClient client;
34 | // private final int REQUEST_CODE = 200;
35 |
36 | public TextView tvCharacterCount;
37 | public EditText etTweet;
38 | public Button bTweet;
39 | private ProgressBar pb;
40 | public String text;
41 | public long reply_uid;
42 |
43 | public static final String TAG = "ComposeActivity";
44 |
45 | // toolbar stuffs
46 | Toolbar compose_toolbar;
47 | TextView compose_toolbar_title;
48 | ImageView compose_toolbar_image, compose_toolbar_cancel_button;
49 | User using_user;
50 |
51 | @Override
52 | protected void onCreate(Bundle savedInstanceState) {
53 | super.onCreate(savedInstanceState);
54 | setContentView(R.layout.activity_compose);
55 | // set the client up
56 | client = TwitterApp.getRestClient();
57 |
58 | // pick up different views
59 | tvCharacterCount = (TextView) findViewById(R.id.tvCharacterCount);
60 | etTweet = (EditText) findViewById(R.id.etTweet);
61 | bTweet = (Button) findViewById(R.id.bTweet);
62 | pb = (ProgressBar) findViewById(R.id.pbLoading);
63 |
64 |
65 | // get the text sent from the intent, this text could be a user to reply to
66 | text = getIntent().getStringExtra("text");
67 | // get/set the toolbar, set the title to home
68 | compose_toolbar = (Toolbar) findViewById(R.id.compose_toolbar);
69 | setSupportActionBar(compose_toolbar);
70 | compose_toolbar.setTitleTextColor(getResources().getColor(R.color.colorWhite));
71 | getSupportActionBar().setTitle(""); // remove the default title
72 | compose_toolbar_title = (TextView) findViewById(R.id.compose_toolbar_title);
73 | compose_toolbar_cancel_button = (ImageView) findViewById(R.id.compose_toolbar_cancel_button);
74 | compose_toolbar_image = (ImageView) findViewById(R.id.compose_toolbar_image);
75 | // cancel button to finish
76 | compose_toolbar_cancel_button.setOnClickListener(new View.OnClickListener() {
77 | @Override
78 | public void onClick(View v) {
79 | finish();
80 | }
81 | });
82 | getUsingUser();
83 |
84 | if (text != null){
85 | // I know I need to better check, but when i had "!text.isEmpty() &&", it would crash the app
86 | // in case the text is not empty (meaning no reply), then we set the text of etTweet to that text (for replies)
87 | etTweet.setText("@"+text+" ");
88 | //place the cursor at the end of the text
89 | etTweet.setSelection(etTweet.getText().length());
90 | reply_uid = getIntent().getLongExtra("reply_uid", 0);
91 | // set title of toolbar
92 | compose_toolbar_title.setText("In reply to "+ text);
93 | }
94 |
95 |
96 |
97 | etTweet.addTextChangedListener(new TextWatcher() {
98 | @Override
99 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
100 |
101 | }
102 |
103 | @Override
104 | public void onTextChanged(CharSequence s, int start, int before, int count) {
105 |
106 | }
107 |
108 | @Override
109 | public void afterTextChanged(Editable s) {
110 | int length = 140 - s.length();
111 | tvCharacterCount.setText(Integer.toString(length));
112 | //String redLightString = getString(R.color.colorRedLight).substring(1);
113 |
114 | int redLight = ContextCompat.getColor(getApplicationContext(), R.color.colorRedLight);
115 | int redDark = ContextCompat.getColor(getApplicationContext(), R.color.colorRed);
116 | int blueLight = ContextCompat.getColor(getApplicationContext(), R.color.colorPrimaryDarkLight);
117 | int whiteColor = ContextCompat.getColor(getApplicationContext(), R.color.colorWhite);
118 |
119 | // Log.i(TAG, String.format("Hey BUTTON %s", bTweet.getText()));
120 | if (length < 20) {
121 | tvCharacterCount.setTextColor(redLight);
122 | if (length < 0) {
123 | bTweet.setEnabled(false);
124 | bTweet.setTextColor(redLight);
125 | bTweet.setBackgroundColor(redDark); // 0xFF992020
126 | } else {
127 | bTweet.setEnabled(true);
128 | bTweet.setTextColor(whiteColor);
129 | bTweet.setBackgroundColor(blueLight); // 0xFF173144
130 | }
131 | } else {
132 | tvCharacterCount.setTextColor(whiteColor);
133 | }
134 | }
135 | });
136 | }
137 |
138 | public void onSubmit(View v) {
139 | pb.setVisibility(ProgressBar.VISIBLE); // set the progress bar to visible
140 | EditText tweet = (EditText) findViewById(R.id.etTweet);
141 | // String text = String.valueOf(tweet.getText());
142 |
143 |
144 | client.sendTweet(tweet.getText().toString(), reply_uid, new JsonHttpResponseHandler() {
145 | @Override
146 | public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
147 | // super.onSuccess(statusCode, headers, response);
148 |
149 | Tweet tweet;
150 | // data.putExtra("code", REQUEST_CODE);
151 | try {
152 | Intent data = new Intent();
153 | tweet = Tweet.fromJSON(response);
154 |
155 | // after getting the tweet parced, we parcialize it with Parcels.wrap(tweet)
156 | data.putExtra("tweet", Parcels.wrap(tweet));
157 | if (text != null) {
158 | // if the text is not null, then we start from another activity
159 | Intent timelineIntent = new Intent(ComposeActivity.this, TimelineActivity.class);
160 | startActivity(timelineIntent);
161 | } else {
162 | setResult(RESULT_OK, data);
163 | finish();
164 | }
165 | } catch (JSONException e) {
166 | //data.putExtra("code", 404);
167 | //setResult(RESULT_CANCELED, data);
168 | e.printStackTrace();
169 | }
170 |
171 | }
172 |
173 | @Override
174 | public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
175 | super.onSuccess(statusCode, headers, response);
176 | }
177 |
178 | @Override
179 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
180 | // super.onFailure(statusCode, headers, throwable, errorResponse);
181 | Log.e(client.TAG, "ERRR ! !" + errorResponse.toString());
182 | throwable.printStackTrace();
183 | }
184 |
185 | @Override
186 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
187 | // super.onFailure(statusCode, headers, throwable, errorResponse);
188 | Log.e(client.TAG, "ERRR ( (" + errorResponse.toString());
189 | throwable.printStackTrace();
190 | }
191 |
192 | @Override
193 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
194 | // super.onFailure(statusCode, headers, responseString, throwable);
195 | Log.e(client.TAG, "ERRR < <" + responseString.toString());
196 | throwable.printStackTrace();
197 | }
198 | });
199 | // closes the activity and returns to first screen
200 | }
201 |
202 | public void getUsingUser(){
203 | client.getUsingUser(new JsonHttpResponseHandler() {
204 | @Override
205 | public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
206 | try {
207 | using_user = User.fromJSON(response);
208 | // set the using user profile image
209 | Glide.with(getBaseContext()).load(using_user.profileImageUrl)
210 | .bitmapTransform(new RoundedCornersTransformation(getBaseContext(), 2000, 0))
211 | .placeholder(R.drawable.ic_person_v1_svg)
212 | .error(R.drawable.ic_person_v1_svg)
213 | .override(2048, 2048)
214 | .into(compose_toolbar_image);
215 | } catch (JSONException e) {
216 | Toast.makeText(getBaseContext(), String.format("Error occurred."), Toast.LENGTH_SHORT).show();
217 | e.printStackTrace();
218 | }
219 | }
220 | @Override
221 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
222 | Log.e(TAG +" : "+ client.TAG, String.format("Error JSONObject: %s" , errorResponse.toString()));
223 | throwable.printStackTrace();
224 |
225 | Toast.makeText(getBaseContext(), String.format("An error occurred."), Toast.LENGTH_SHORT).show();
226 | }
227 | @Override
228 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
229 | Log.e(TAG +" : "+ client.TAG, String.format("Error JSONArray: %s" , errorResponse.toString()));
230 | throwable.printStackTrace();
231 |
232 | Toast.makeText(getBaseContext(), String.format("An error occurred."), Toast.LENGTH_SHORT).show();
233 | }
234 | @Override
235 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
236 | Log.e(TAG +" : "+ client.TAG, String.format("Error String: %s" , responseString));
237 | throwable.printStackTrace();
238 |
239 | Toast.makeText(getBaseContext(), String.format("An error occurred."), Toast.LENGTH_SHORT).show();
240 | }
241 | });
242 | }
243 | }
244 |
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/LoginActivity.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.view.Menu;
6 | import android.view.View;
7 |
8 | import com.codepath.oauth.OAuthLoginActionBarActivity;
9 |
10 | public class LoginActivity extends OAuthLoginActionBarActivity {
11 |
12 | @Override
13 | protected void onCreate(Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 | setContentView(R.layout.activity_login);
16 | }
17 |
18 |
19 | // Inflate the menu; this adds items to the action bar if it is present.
20 | @Override
21 | public boolean onCreateOptionsMenu(Menu menu) {
22 | getMenuInflater().inflate(R.menu.login, menu);
23 | return true;
24 | }
25 |
26 | // OAuth authenticated successfully, launch primary authenticated activity
27 | // i.e Display application "homepage"
28 | @Override
29 | public void onLoginSuccess() {
30 | Intent timeline = new Intent(this, TimelineActivity.class);
31 | startActivity(timeline);
32 | }
33 |
34 | // OAuth authentication flow failed, handle the error
35 | // i.e Display an error dialog or toast
36 | @Override
37 | public void onLoginFailure(Exception e) {
38 | e.printStackTrace();
39 | }
40 |
41 | // Click handler method for the button used to start OAuth flow
42 | // Uses the client to initiate OAuth authorization
43 | // This should be tied to a button used to login
44 | public void loginToRest(View view) {
45 | getClient().connect();
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/MyDatabase.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate;
2 |
3 | import com.raizlabs.android.dbflow.annotation.Database;
4 |
5 | @Database(name = MyDatabase.NAME, version = MyDatabase.VERSION)
6 | public class MyDatabase {
7 |
8 | public static final String NAME = "RestClientDatabase";
9 |
10 | public static final int VERSION = 1;
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/TimelineActivity.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate;
2 |
3 | import android.content.Intent;
4 | import android.content.res.ColorStateList;
5 | import android.os.Bundle;
6 | import android.support.design.widget.FloatingActionButton;
7 | import android.support.design.widget.TabLayout;
8 | import android.support.v4.content.ContextCompat;
9 | import android.support.v4.view.ViewPager;
10 | import android.support.v7.app.AppCompatActivity;
11 | import android.support.v7.widget.Toolbar;
12 | import android.util.Log;
13 | import android.view.View;
14 | import android.view.animation.AccelerateInterpolator;
15 | import android.view.animation.DecelerateInterpolator;
16 | import android.widget.ImageView;
17 | import android.widget.ProgressBar;
18 | import android.widget.TextView;
19 | import android.widget.Toast;
20 |
21 | import com.bumptech.glide.Glide;
22 | import com.codepath.apps.restclienttemplate.fragments.HomeTimelineFragment;
23 | import com.codepath.apps.restclienttemplate.fragments.TweetsPagerAdapter;
24 | import com.codepath.apps.restclienttemplate.models.Tweet;
25 | import com.codepath.apps.restclienttemplate.models.User;
26 | import com.loopj.android.http.JsonHttpResponseHandler;
27 |
28 | import org.json.JSONArray;
29 | import org.json.JSONException;
30 | import org.json.JSONObject;
31 | import org.parceler.Parcels;
32 |
33 | import cz.msebera.android.httpclient.Header;
34 | import jp.wasabeef.glide.transformations.RoundedCornersTransformation;
35 |
36 | public class TimelineActivity extends AppCompatActivity {
37 |
38 | // toolbar stuffs
39 | Toolbar home_toolbar;
40 | TextView home_toolbar_title;
41 | ImageView home_toolbar_image;
42 | User using_user;
43 |
44 | // twitter client
45 | private TwitterClient client;
46 | public ViewPager vpPager;
47 | public TweetsPagerAdapter pagerAdapter;
48 |
49 | private final int REQUEST_CODE = 200;
50 |
51 | public FloatingActionButton fabCompose;
52 |
53 |
54 | private ProgressBar pb; // TODO - Take care of pb, place it in fragment or something!
55 |
56 | public static final String TAG = "TimelineActivityTAG";
57 |
58 | // For scroll hiding and showing
59 | // https://mzgreen.github.io/2015/02/15/How-to-hideshow-Toolbar-when-list-is-scroling%28part1%29/
60 |
61 | @Override
62 | protected void onCreate(Bundle savedInstanceState) {
63 | super.onCreate(savedInstanceState);
64 | setContentView(R.layout.activity_timeline);
65 | // get the client
66 | client = TwitterApp.getRestClient();
67 |
68 | // get/set the toolbar, set the title to home
69 | home_toolbar = (Toolbar) findViewById(R.id.home_toolbar);
70 | setSupportActionBar(home_toolbar);
71 | home_toolbar.setTitleTextColor(getResources().getColor(R.color.colorWhite));
72 | getSupportActionBar().setTitle(""); // remove the default title
73 | home_toolbar_title = (TextView) findViewById(R.id.home_toolbar_title);
74 | home_toolbar_image = (ImageView) findViewById(R.id.home_toolbar_image);
75 | getUsingUser();
76 | // create the link to the using user's profile
77 | home_toolbar_image.setOnClickListener(new View.OnClickListener() {
78 | @Override
79 | public void onClick(View v) {
80 | onProfileView();
81 | }
82 | });
83 |
84 | // attach the ProgressBar and the FloatingActionBar
85 | pb = (ProgressBar) findViewById(R.id.pbLoading);
86 | pb.setVisibility(ProgressBar.INVISIBLE); // remove the progress bar
87 | fabCompose = (FloatingActionButton) findViewById(R.id.fabCompose);
88 |
89 | // get the view pager
90 | vpPager = (ViewPager) findViewById(R.id.viewpager);
91 | // set the adapter for the pager
92 | pagerAdapter = new TweetsPagerAdapter(getSupportFragmentManager(), this);
93 | vpPager.setAdapter(pagerAdapter);
94 | // set up the tab layout to use the view pager
95 | TabLayout tabLayout = (TabLayout) findViewById(R.id.sliding_tabs);
96 | tabLayout.setupWithViewPager(vpPager);
97 | tabLayout.setSelectedTabIndicatorColor(ContextCompat.getColor(getBaseContext(), R.color.colorAccent));
98 | tabLayout.setBackgroundColor(ContextCompat.getColor(getBaseContext(), R.color.colorPrimary));
99 | tabLayout.setTabTextColors(ColorStateList.valueOf(ContextCompat.getColor(getBaseContext(), R.color.colorWhite)));
100 |
101 | fabCompose.setOnClickListener(new View.OnClickListener() {
102 | @Override
103 | public void onClick(View v) {
104 | composeMessage(null);
105 | }
106 | });
107 | }
108 |
109 | public void getUsingUser(){
110 | client.getUsingUser(new JsonHttpResponseHandler() {
111 | @Override
112 | public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
113 | try {
114 | using_user = User.fromJSON(response);
115 | // set the using user profile image
116 | Glide.with(getBaseContext()).load(using_user.profileImageUrl)
117 | .bitmapTransform(new RoundedCornersTransformation(getBaseContext(), 2000, 0))
118 | .placeholder(R.drawable.ic_person_v1_svg)
119 | .error(R.drawable.ic_person_v1_svg)
120 | .override(2048, 2048)
121 | .into(home_toolbar_image);
122 | } catch (JSONException e) {
123 | Toast.makeText(getBaseContext(), String.format("Error occurred."), Toast.LENGTH_SHORT).show();
124 | e.printStackTrace();
125 | }
126 | }
127 | @Override
128 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
129 | Log.e(TAG +" : "+ client.TAG, String.format("Error JSONObject: %s" , errorResponse.toString()));
130 | throwable.printStackTrace();
131 |
132 | Toast.makeText(getBaseContext(), String.format("An error occurred."), Toast.LENGTH_SHORT).show();
133 | }
134 | @Override
135 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
136 | Log.e(TAG +" : "+ client.TAG, String.format("Error JSONArray: %s" , errorResponse.toString()));
137 | throwable.printStackTrace();
138 |
139 | Toast.makeText(getBaseContext(), String.format("An error occurred."), Toast.LENGTH_SHORT).show();
140 | }
141 | @Override
142 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
143 | Log.e(TAG +" : "+ client.TAG, String.format("Error String: %s" , responseString));
144 | throwable.printStackTrace();
145 |
146 | Toast.makeText(getBaseContext(), String.format("An error occurred."), Toast.LENGTH_SHORT).show();
147 | }
148 | });
149 | }
150 |
151 | public void onProfileView() {
152 | // launch the profile view
153 | Intent i = new Intent(TimelineActivity.this, UserProfileActivity.class);
154 | i.putExtra("user_uid", using_user.uid);
155 | i.putExtra("screenName", using_user.screenName);
156 | i.putExtra("using_user", true);
157 | startActivity(i);
158 | }
159 |
160 | public void composeMessage(String text) {
161 | Intent i = new Intent(TimelineActivity.this, ComposeActivity.class);
162 | i.putExtra("text", text);
163 | startActivityForResult(i, REQUEST_CODE);
164 | }
165 |
166 | @Override
167 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
168 | // REQUEST_CODE is defined above
169 | Log.d(TAG, String.format("%s %s %s RESULT_OK: %s", "TIMELINE START", resultCode, requestCode, RESULT_OK));
170 | if (resultCode == RESULT_OK && requestCode == REQUEST_CODE) {
171 | // Extract name value from result extras
172 | Tweet tweet = Parcels.unwrap(data.getParcelableExtra("tweet"));
173 | // add the tweet to the set of tweets at position 0
174 | ((HomeTimelineFragment) pagerAdapter.getItem(vpPager.getCurrentItem())).addTweet(tweet);
175 | // fragmentTweetsList.addItemOne(tweet, 0);
176 | pb.setVisibility(ProgressBar.INVISIBLE); // remove the progress bar
177 | }
178 | }
179 |
180 | private void hideViews() {
181 | // hides the toolbar
182 | home_toolbar.animate().translationY(-home_toolbar.getHeight()).setInterpolator(new AccelerateInterpolator(2));
183 | }
184 |
185 | private void showViews() {
186 | // shows the toolbar
187 | home_toolbar.animate().translationY(0).setInterpolator(new DecelerateInterpolator(2));
188 | }
189 | /**/
190 |
191 | /*
192 | @Override
193 | public boolean onOptionsItemSelected(MenuItem item) {
194 | // Handle presses on the action bar items
195 | switch (item.getItemId()) {
196 | case R.id.aCompose:
197 | composeMessage(null);
198 | return true;
199 | default:
200 | return super.onOptionsItemSelected(item);
201 | }
202 | }*/
203 |
204 | /*
205 | REALLY GOOD HELP I GOT FROM STACK OVERFLOW
206 | ===========================================
207 | LINK: https://stackoverflow.com/questions/26543131/how-to-implement-endless-list-with-recyclerview
208 |
209 | LinearLayoutManager mLayoutManager;
210 | mLayoutManager = new LinearLayoutManager(this);
211 | mRecyclerView.setLayoutManager(mLayoutManager);
212 | * */
213 | }
214 |
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/TweetDetailsActivity.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v4.content.ContextCompat;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.support.v7.widget.Toolbar;
8 | import android.text.Html;
9 | import android.view.View;
10 | import android.widget.ImageView;
11 | import android.widget.TextView;
12 | import android.widget.Toast;
13 |
14 | import com.bumptech.glide.Glide;
15 | import com.codepath.apps.restclienttemplate.models.TweetText;
16 | import com.loopj.android.http.AsyncHttpResponseHandler;
17 |
18 | import cz.msebera.android.httpclient.Header;
19 | import jp.wasabeef.glide.transformations.RoundedCornersTransformation;
20 |
21 | public class TweetDetailsActivity extends AppCompatActivity {
22 | public ImageView ivProfileImageD, ibReplyD, ivRetweetD, ivLikeD, ivTweetedImageD;
23 | public TextView tvNameD, tvScreenNameD, tvBodyD, tvCreatedAtD, tvRetweetsD, tvLikesD;
24 | public long tUid;
25 | public boolean tRetweeted, tLiked, tMediaFound;
26 | private static TwitterClient client;
27 |
28 | public String tName, tScreenName, tBody, tCreatedAt, tProfileImageUrl;
29 | public long tRetweets, tLikes;
30 |
31 | // toolbar stuffs
32 | Toolbar tweet_detail_toolbar;
33 | TextView tweet_detail_toolbar_title;
34 | ImageView tweet_detail_toolbar_back_button;
35 |
36 | @Override
37 | protected void onCreate(Bundle savedInstanceState) {
38 | super.onCreate(savedInstanceState);
39 | setContentView(R.layout.activity_tweet_details);
40 |
41 | // get the twitter client
42 | client = TwitterApp.getRestClient();
43 |
44 | // get/set the toolbar, set the title to home
45 | tweet_detail_toolbar = (Toolbar) findViewById(R.id.tweet_detail_toolbar);
46 | setSupportActionBar(tweet_detail_toolbar);
47 | tweet_detail_toolbar.setTitleTextColor(getResources().getColor(R.color.colorWhite));
48 | getSupportActionBar().setTitle(""); // remove the default title
49 | tweet_detail_toolbar_title = (TextView) findViewById(R.id.tweet_detail_toolbar_title);
50 | tweet_detail_toolbar_back_button = (ImageView) findViewById(R.id.tweet_detail_toolbar_back_button);
51 | // create the link to the using user's profile
52 | tweet_detail_toolbar_back_button.setOnClickListener(new View.OnClickListener() {
53 | @Override
54 | public void onClick(View v) {
55 | finish();
56 | }
57 | });
58 |
59 | // set those variables to values received from the intent
60 | tName = getIntent().getStringExtra("tName");
61 | tScreenName = getIntent().getStringExtra("tScreenName");
62 | tBody = getIntent().getStringExtra("tBody");
63 | tCreatedAt = getIntent().getStringExtra("tCreatedAt");
64 | tProfileImageUrl = getIntent().getStringExtra("tProfileImageUrl");
65 | tRetweets = getIntent().getLongExtra("tRetweets", 0);
66 | tLikes = getIntent().getLongExtra("tLikes", 0);
67 | tUid = getIntent().getLongExtra("tUid", -1);
68 | tLiked = getIntent().getBooleanExtra("tLiked", false);
69 | tRetweeted = getIntent().getBooleanExtra("tRetweeted", false);
70 | tMediaFound = getIntent().getBooleanExtra("tMediaFound", false);
71 |
72 | // create the correct variables
73 | ivProfileImageD = (ImageView) findViewById(R.id.ivProfileImageD);
74 | tvNameD = (TextView) findViewById(R.id.tvNameD);
75 | tvScreenNameD = (TextView) findViewById(R.id.tvScreenNameD);
76 | tvBodyD = (TextView) findViewById(R.id.tvBodyD);
77 | tvCreatedAtD = (TextView) findViewById(R.id.tvCreatedAtD);
78 | ibReplyD = (ImageView) findViewById(R.id.ibReplyD);
79 |
80 | tvRetweetsD = (TextView) findViewById(R.id.tvRetweetsD);
81 | tvLikesD = (TextView) findViewById(R.id.tvLikesD);
82 | ivRetweetD = (ImageView) findViewById(R.id.ivRetweetD);
83 | ivLikeD = (ImageView) findViewById(R.id.ivLoveTweetD);
84 | ivTweetedImageD = (ImageView) findViewById(R.id.ivTweetedImageD);
85 |
86 | // set the texts
87 | tvNameD.setText(tName);
88 | tvScreenNameD.setText("@"+tScreenName);
89 | TweetText finalTweet = new TweetText(tBody);
90 | tvBodyD.setText(Html.fromHtml(finalTweet.finalText));
91 | tvCreatedAtD.setText(tCreatedAt);
92 | tvRetweetsD.setText(Long.toString(tRetweets));
93 | tvLikesD.setText(Long.toString(tLikes));
94 |
95 | if (tRetweeted) ivRetweetD.setImageDrawable(ContextCompat.getDrawable(TweetDetailsActivity.this, R.drawable.ic_retweet_green_svg));
96 | else ivRetweetD.setImageDrawable(ContextCompat.getDrawable(TweetDetailsActivity.this, R.drawable.ic_retweet_svg));
97 | if (tLiked) ivLikeD.setImageDrawable(ContextCompat.getDrawable(TweetDetailsActivity.this, R.drawable.ic_heart_solid_red_svg));
98 | else ivLikeD.setImageDrawable(ContextCompat.getDrawable(TweetDetailsActivity.this, R.drawable.ic_heart_clear_light_blue_svg));
99 | if (tMediaFound) {
100 | ivTweetedImageD.setVisibility(View.VISIBLE);
101 | Glide.with(TweetDetailsActivity.this).load(getIntent().getStringExtra("tMediaUrlHTTPS"))
102 | .bitmapTransform(new RoundedCornersTransformation(TweetDetailsActivity.this, 20, 0))
103 | .placeholder(R.drawable.ic_picture_placeholder_svg)
104 | .error(R.drawable.ic_picture_placeholder_svg)
105 | .into(ivTweetedImageD);
106 | } else {
107 | ivTweetedImageD.setVisibility(View.GONE);
108 | }
109 |
110 | // set the image profile
111 | Glide.with(this).load(tProfileImageUrl)
112 | .bitmapTransform(new RoundedCornersTransformation(this, 2000, 0))
113 | .placeholder(R.drawable.ic_person_v3_svg)
114 | .error(R.drawable.ic_person_v3_svg)
115 | .into(ivProfileImageD);
116 | // .override(2048, 2048)
117 |
118 | // Reply listener event
119 | ibReplyD.setOnClickListener(new View.OnClickListener() {
120 | @Override
121 | public void onClick(View v) {
122 | Intent i = new Intent(TweetDetailsActivity.this, ComposeActivity.class);
123 | i.putExtra("text", tScreenName);
124 | startActivity(i);
125 | }
126 | });
127 |
128 | // Retweet listener event
129 | ivRetweetD.setOnClickListener(new View.OnClickListener() {
130 | @Override
131 | public void onClick(View v) {
132 | if (!tRetweeted){
133 | client.retweetTweet(tUid, new AsyncHttpResponseHandler() {
134 | @Override
135 | public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
136 | tvRetweetsD.setText(Long.toString(tRetweets + 1));
137 | tRetweeted = true;
138 | ivRetweetD.setImageDrawable(ContextCompat.getDrawable(TweetDetailsActivity.this, R.drawable.ic_retweet_green_svg));
139 | }
140 |
141 | @Override
142 | public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
143 | Toast.makeText(TweetDetailsActivity.this, "Error occured while processing retweet action", Toast.LENGTH_SHORT).show();
144 | }
145 | });
146 | } else {
147 | client.unretweetTweet(tUid, new AsyncHttpResponseHandler() {
148 | @Override
149 | public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
150 | tvRetweetsD.setText(Long.toString(tRetweets));
151 | tRetweeted = false;
152 | ivRetweetD.setImageDrawable(ContextCompat.getDrawable(TweetDetailsActivity.this, R.drawable.ic_retweet_svg));
153 | }
154 |
155 | @Override
156 | public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
157 | Toast.makeText(TweetDetailsActivity.this, "Error occured while processing unretweet action", Toast.LENGTH_SHORT).show();
158 | }
159 | });
160 | }
161 | }
162 | });
163 |
164 | // Like listener event
165 | ivLikeD.setOnClickListener(new View.OnClickListener() {
166 | @Override
167 | public void onClick(View v) {
168 | if (!tLiked){
169 | client.likeTweet(tUid, new AsyncHttpResponseHandler() {
170 | @Override
171 | public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
172 | tvLikesD.setText(Long.toString(tLikes + 1));
173 | tLiked = true;
174 | ivLikeD.setImageDrawable(ContextCompat.getDrawable(TweetDetailsActivity.this, R.drawable.ic_heart_solid_red_svg));
175 | }
176 |
177 | @Override
178 | public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
179 | Toast.makeText(TweetDetailsActivity.this, "Error occured while processing retweet action", Toast.LENGTH_SHORT).show();
180 | }
181 | });
182 | } else {
183 | client.unlikeTweet(tUid, new AsyncHttpResponseHandler() {
184 | @Override
185 | public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
186 | tvLikesD.setText(Long.toString(tLikes));
187 | tLiked = false;
188 | ivLikeD.setImageDrawable(ContextCompat.getDrawable(TweetDetailsActivity.this, R.drawable.ic_heart_clear_light_blue_svg));
189 | }
190 |
191 | @Override
192 | public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
193 | Toast.makeText(TweetDetailsActivity.this, "Error occured while processing unretweet action", Toast.LENGTH_SHORT).show();
194 | }
195 | });
196 | }
197 | }
198 | });
199 |
200 | ivProfileImageD.setOnClickListener(new View.OnClickListener() {
201 | @Override
202 | public void onClick(View v) {
203 | Intent i = new Intent(TweetDetailsActivity.this, UserProfileActivity.class);
204 | i.putExtra("user_uid", tUid);
205 | i.putExtra("screenName", tScreenName);
206 | startActivity(i);
207 | }
208 | });
209 | }
210 |
211 | @Override
212 | public void onBackPressed() {
213 | // super.onBackPressed();
214 | finish();
215 | // Intent i = new Intent(TweetDetailsActivity.this, TimelineActivity.class);
216 | // startActivity(i);
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/TwitterApp.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 |
6 | import com.raizlabs.android.dbflow.config.FlowConfig;
7 | import com.raizlabs.android.dbflow.config.FlowLog;
8 | import com.raizlabs.android.dbflow.config.FlowManager;
9 |
10 | /*
11 | * This is the Android application itself and is used to configure various settings
12 | * including the image cache in memory and on disk. This also adds a singleton
13 | * for accessing the relevant rest client.
14 | *
15 | * RestClient client = RestApplication.getRestClient();
16 | * // use client to send requests to API
17 | *
18 | */
19 | public class TwitterApp extends Application {
20 | private static Context context;
21 |
22 | @Override
23 | public void onCreate() {
24 | super.onCreate();
25 |
26 | FlowManager.init(new FlowConfig.Builder(this).build());
27 | FlowLog.setMinimumLoggingLevel(FlowLog.Level.V);
28 |
29 | TwitterApp.context = this;
30 | }
31 |
32 | public static TwitterClient getRestClient() {
33 | return (TwitterClient) TwitterClient.getInstance(TwitterClient.class, TwitterApp.context);
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/TwitterClient.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate;
2 |
3 | import android.content.Context;
4 |
5 | import com.codepath.oauth.OAuthBaseClient;
6 | import com.github.scribejava.apis.TwitterApi;
7 | import com.github.scribejava.core.builder.api.BaseApi;
8 | import com.loopj.android.http.AsyncHttpResponseHandler;
9 | import com.loopj.android.http.RequestParams;
10 |
11 | /*
12 | *
13 | * This is the object responsible for communicating with a REST API.
14 | * Specify the constants below to change the API being communicated with.
15 | * See a full list of supported API classes:
16 | * https://github.com/scribejava/scribejava/tree/master/scribejava-apis/src/main/java/com/github/scribejava/apis
17 | * Key and Secret are provided by the developer site for the given API i.e dev.twitter.com
18 | * Add methods for each relevant endpoint in the API.
19 | *
20 | * NOTE: You may want to rename this object based on the service i.e TwitterClient or FlickrClient
21 | *
22 | */
23 | public class TwitterClient extends OAuthBaseClient {
24 | private static final BaseApi REST_API_INSTANCE = TwitterApi.instance(); // Change this
25 | private static final String REST_URL = "https://api.twitter.com/1.1"; // Change this, base API URL
26 | private static final String REST_CONSUMER_KEY = "PXZuhfIR5Vk7hi1X4BKOzf8Jv"; // Change this TODO - GIT IGNORE THIS FILE OR CHANGE KEY WHEN UPLOAD
27 | private static final String REST_CONSUMER_SECRET = "2xCkNFtQy4evatLxCblAI8RlbSPuFsXLb207NzZVAp5jcByxU3"; // Change this TODO - GIT IGNORE THIS FILE
28 |
29 | // TAG for logging stuffs
30 | public static final String TAG = "TwitterClient";
31 |
32 | // Landing page to indicate the OAuth flow worked in case Chrome for Android 25+ blocks navigation back to the app.
33 | private static final String FALLBACK_URL = "https://codepath.github.io/android-rest-client-template/success.html";
34 |
35 | // See https://developer.chrome.com/multidevice/android/intents
36 | private static final String REST_CALLBACK_URL_TEMPLATE = "intent://%s#Intent;action=android.intent.action.VIEW;scheme=%s;package=%s;S.browser_fallback_url=%s;end";
37 |
38 | public TwitterClient(Context context) {
39 | super(context, REST_API_INSTANCE,
40 | REST_URL,
41 | REST_CONSUMER_KEY, // key
42 | REST_CONSUMER_SECRET,
43 | String.format(REST_CALLBACK_URL_TEMPLATE, context.getString(R.string.intent_host),
44 | context.getString(R.string.intent_scheme), context.getPackageName(), FALLBACK_URL));
45 | }
46 |
47 | // DEFINE METHODS for different API endpoints here
48 | public void getHomeTimeline(int count, AsyncHttpResponseHandler handler) {
49 | String apiUrl = getApiUrl("statuses/home_timeline.json");
50 | // Can specify query string params directly or through RequestParams.
51 | RequestParams params = new RequestParams();
52 | params.put("count", count);
53 | params.put("include_entities", true);
54 | params.put("exclude_replies", false);
55 | // params.put("since_id", 1);
56 | // params.put("max_id", 1);
57 | client.get(apiUrl, params, handler);
58 | }
59 |
60 | public void addToTimeline(long maxid, AsyncHttpResponseHandler handler) {
61 | String apiUrl = getApiUrl("statuses/home_timeline.json");
62 | RequestParams params = new RequestParams();
63 | params.put("count", 15);
64 | params.put("max_id", maxid-1);
65 | params.put("include_entities", true);
66 | client.get(apiUrl, params, handler);
67 | }
68 |
69 | public void getMentionsTimeline(int count, AsyncHttpResponseHandler handler) {
70 | String apiUrl = getApiUrl("statuses/mentions_timeline.json");
71 | // Can specify query string params directly or through RequestParams.
72 | RequestParams params = new RequestParams();
73 | params.put("count", count);
74 | params.put("include_entities", true);
75 | client.get(apiUrl, params, handler);
76 | }
77 |
78 | public void addToMentionsTimeline(long maxid, AsyncHttpResponseHandler handler) {
79 | String apiUrl = getApiUrl("statuses/mentions_timeline.json");
80 | RequestParams params = new RequestParams();
81 | params.put("count", 15);
82 | params.put("max_id", maxid-1);
83 | params.put("include_entities", true);
84 | client.get(apiUrl, params, handler);
85 | }
86 |
87 | // Can specify query string params directly or through RequestParams.
88 | public void sendTweet(String message, long reply_uid, AsyncHttpResponseHandler handler) {
89 | String apiUrl = getApiUrl("statuses/update.json");
90 | RequestParams params = new RequestParams();
91 | params.put("status", message);
92 | params.put("in_reply_to_status_id", reply_uid);
93 | client.post(apiUrl, params, handler);
94 | }
95 |
96 | public void retweetTweet(long uid, AsyncHttpResponseHandler handler) {
97 | // String apiUrl = getApiUrl("statuses/retweet/:id.json"); // TODO - does this work?
98 | String apiUrl = getApiUrl("statuses/retweet/"+Long.toString(uid)+".json");
99 | // RequestParams params = new RequestParams();
100 | // params.put("id", uid);
101 | client.post(apiUrl, null, handler);
102 | }
103 |
104 | public void unretweetTweet(long uid, AsyncHttpResponseHandler handler) {
105 |
106 | // String apiUrl = getApiUrl("statuses/unretweet/:id.json"); // TODO - does this work?
107 | String apiUrl = getApiUrl("statuses/unretweet/"+Long.toString(uid)+".json"); // TODO - does this work?
108 | //"statuses/unretweet/"+Long.toString(uid)+".json"
109 | // RequestParams params = new RequestParams();
110 | // params.put("id", uid);
111 | client.post(apiUrl, null, handler);
112 | }
113 |
114 |
115 | public void likeTweet(long uid, AsyncHttpResponseHandler handler) {
116 | String apiUrl = getApiUrl("favorites/create.json");
117 | RequestParams params = new RequestParams();
118 | params.put("id", uid);
119 | client.post(apiUrl, params, handler);
120 | }
121 |
122 | public void unlikeTweet(long uid, AsyncHttpResponseHandler handler) {
123 | String apiUrl = getApiUrl("favorites/destroy.json");
124 | RequestParams params = new RequestParams();
125 | params.put("id", uid);
126 | client.post(apiUrl, params, handler);
127 | }
128 |
129 | public void getUsingUser(AsyncHttpResponseHandler handler) {
130 | String apiUrl = getApiUrl("account/verify_credentials.json");
131 | RequestParams params = new RequestParams();
132 | params.put("include_entities", true);
133 | client.get(apiUrl, params, handler);
134 | }
135 |
136 | public void user(long uid, String screenName, AsyncHttpResponseHandler handler) {
137 | // returns the info about a users profile
138 | String apiUrl = getApiUrl("users/show.json");
139 | RequestParams params = new RequestParams();
140 | params.put("user_id", uid);
141 | params.put("screen_name", screenName);
142 | params.put("include_entities", true);
143 | client.get(apiUrl, params, handler);
144 | }
145 |
146 | public void getUserTimeline(String screenName, long count, AsyncHttpResponseHandler handler) {
147 | // returns the statuses of the user specified by the uid, set count to 20 and maxId to LONG.MAXVALUE for now
148 | String apiUrl = getApiUrl("statuses/user_timeline.json");
149 | RequestParams params = new RequestParams();
150 | params.put("screen_name", screenName);
151 | params.put("count", count); // how many tweets to get
152 | // params.put("include_rts", true);
153 | // params.put("exclude_replies", false);
154 | client.get(apiUrl, params, handler);
155 | }
156 |
157 | public void addToUserTimeline(String screenName, long maxId, AsyncHttpResponseHandler handler) {
158 | // returns the statuses of the user specified by the uid, set count to 20 and maxId to LONG.MAXVALUE for now
159 | String apiUrl = getApiUrl("statuses/user_timeline.json");
160 | RequestParams params = new RequestParams();
161 | params.put("max_id", maxId); // used for infinite paginations
162 | params.put("screen_name", screenName);
163 | params.put("count", 30); // how many tweets to get
164 | params.put("include_rts", true);
165 | params.put("exclude_replies", false);
166 | client.get(apiUrl, params, handler);
167 | }
168 |
169 | public void userLookup(String screenNames, AsyncHttpResponseHandler handler) {
170 | // returns whether the current user follows the users in the list of longs
171 | // screenNames is a concatenation of user screen names separated by just commas
172 | String apiUrl = getApiUrl("friendships/lookup.json");
173 | RequestParams params = new RequestParams();
174 | params.put("screen_name", screenNames);
175 | client.get(apiUrl, params, handler);
176 | }
177 |
178 | public void followToggle(String screenName, boolean following, AsyncHttpResponseHandler handler){
179 | String apiUrl;
180 | if (!following) apiUrl = getApiUrl("friendships/create.json");
181 | else apiUrl = getApiUrl("friendships/destroy.json");
182 | RequestParams params = new RequestParams();
183 | params.put("screen_name", screenName);
184 | client.post(apiUrl, params, handler);
185 | }
186 |
187 | public void userFollowers(){
188 | // https://dev.twitter.com/rest/reference/get/followers/list
189 | // get the users followers
190 | String apiUrl = getApiUrl("followers/list.json");
191 | RequestParams params = new RequestParams();
192 | }
193 |
194 | public void userFollowings(){
195 | // https://dev.twitter.com/rest/reference/get/friends/list
196 | // get the users followings
197 | String apiUrl = getApiUrl("friends/list.json");
198 | RequestParams params = new RequestParams();
199 | }
200 |
201 |
202 |
203 | /* 1. Define the endpoint URL with getApiUrl and pass a relative path to the endpoint
204 | * i.e getApiUrl("statuses/home_timeline.json");
205 | * 2. Define the parameters to pass to the request (query or body)
206 | * i.e RequestParams params = new RequestParams("foo", "bar");
207 | * 3. Define the request method and make a call to the client
208 | * i.e client.get(apiUrl, params, handler);
209 | * i.e client.post(apiUrl, params, handler);
210 | */
211 | }
212 |
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/UserProfileActivity.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate;
2 |
3 | import android.os.Bundle;
4 | import android.support.design.widget.FloatingActionButton;
5 | import android.support.v4.app.FragmentTransaction;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.util.Log;
8 | import android.view.View;
9 | import android.widget.Button;
10 | import android.widget.ImageView;
11 | import android.widget.ProgressBar;
12 | import android.widget.TextView;
13 | import android.widget.Toast;
14 |
15 | import com.bumptech.glide.Glide;
16 | import com.codepath.apps.restclienttemplate.fragments.UserTimelineFragment;
17 | import com.codepath.apps.restclienttemplate.models.User;
18 | import com.loopj.android.http.JsonHttpResponseHandler;
19 |
20 | import org.json.JSONArray;
21 | import org.json.JSONException;
22 | import org.json.JSONObject;
23 |
24 | import java.text.SimpleDateFormat;
25 | import java.util.Calendar;
26 |
27 | import cz.msebera.android.httpclient.Header;
28 | import jp.wasabeef.glide.transformations.RoundedCornersTransformation;
29 |
30 | public class UserProfileActivity extends AppCompatActivity {
31 | public User user;
32 | private TwitterClient client;
33 | public Long userId;
34 | public String screenName, TAG = "UserProfileActivity";
35 |
36 | // logging times
37 | Calendar CAL = Calendar.getInstance();
38 | SimpleDateFormat HMS = new SimpleDateFormat("HH:mm:ss");
39 |
40 | private ProgressBar pb;
41 |
42 | // get the views
43 | public TextView tvUserNameP, tvScreenNameP, tvSinceP, tvFollowersCount, tvFollowers, tvFollowingsCount, tvFollowings, tvUserDescription, tvFollowsYou;
44 | public ImageView bannerImage;
45 | public Button followButton;
46 | public FloatingActionButton ivProfileImageP;
47 |
48 | @Override
49 | protected void onCreate(Bundle savedInstanceState) {
50 | super.onCreate(savedInstanceState);
51 | setContentView(R.layout.activity_user_profile);
52 |
53 | client = TwitterApp.getRestClient();
54 |
55 | pb = (ProgressBar) findViewById(R.id.pbLoading);
56 |
57 | userId = getIntent().getLongExtra("user_uid", -1);
58 | screenName = getIntent().getStringExtra("screenName");
59 | if (userId == -1){
60 | onError(0, null);
61 | } else if (screenName == null) {
62 | onError(0, "Screen name was null");
63 | }
64 |
65 | // set the title to the screen name
66 | // getSupportActionBar().setTitle(screenName);
67 |
68 | tvUserNameP = (TextView) findViewById(R.id.tvUserNameP);
69 | tvScreenNameP = (TextView) findViewById(R.id.tvScreenNameP);
70 | tvSinceP = (TextView) findViewById(R.id.tvSinceP);
71 | tvFollowersCount = (TextView) findViewById(R.id.tvFollowersCount);
72 | tvFollowers = (TextView) findViewById(R.id.tvFollowers);
73 | tvFollowingsCount = (TextView) findViewById(R.id.tvFollowingsCount);
74 | tvFollowings = (TextView) findViewById(R.id.tvFollowings);
75 | tvUserDescription = (TextView) findViewById(R.id.tvUserDescription);
76 | tvFollowsYou = (TextView) findViewById(R.id.tvFollowsYou);
77 |
78 | followButton = (Button) findViewById(R.id.followButton);
79 |
80 | ivProfileImageP = (FloatingActionButton) findViewById(R.id.ivProfileImageP);
81 | bannerImage = (ImageView) findViewById(R.id.bannerImage);
82 | // get the user from the following request
83 | client.user(userId, screenName, new JsonHttpResponseHandler() {
84 | @Override
85 | public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
86 | try {
87 | user = User.fromJSON(response);
88 |
89 | // populate the headline, see method below
90 | populateUserHeadline(user);
91 |
92 | } catch (JSONException e) {
93 | onError(1, null);
94 | e.printStackTrace();
95 | }
96 |
97 | // create the fragment
98 | UserTimelineFragment userTimelineFragment = UserTimelineFragment.newInstance(screenName);
99 | // display the user timeline fragment inside the container
100 | FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
101 | // make change
102 | ft.replace(R.id.flContainer, userTimelineFragment);
103 | // commit
104 | ft.commit();
105 |
106 | pb.setVisibility(View.INVISIBLE); // remove the progress bar
107 | }
108 | });
109 |
110 | }
111 |
112 | public void populateUserHeadline(final User user) {
113 | // populate the screen with info gathered
114 | tvUserNameP.setText(user.name);
115 | tvScreenNameP.setText("@"+user.screenName);
116 | tvSinceP.setText("On Twitter since " + user.createdAt);
117 | tvFollowersCount.setText(Long.toString(user.followersCount));
118 | tvFollowingsCount.setText(Long.toString(user.followingsCount));
119 | tvUserDescription.setText(user.description);
120 | if (user.description.length() <= 1) {
121 | tvUserDescription.setVisibility(View.GONE);
122 | }
123 | // add the images
124 | Glide.with(getBaseContext()).load(user.profileImageUrl)
125 | .bitmapTransform(new RoundedCornersTransformation(getBaseContext(), 2000, 0))
126 | .placeholder(R.drawable.ic_person_v1_svg)
127 | .error(R.drawable.ic_person_v1_svg)
128 | .into(ivProfileImageP);
129 |
130 | Glide.with(getBaseContext()).load(user.profileBannerUrl)
131 | .placeholder(Integer.parseInt(user.profileBackgroundColor, 16)+0xFF000000)
132 | .error(Integer.parseInt(user.profileBackgroundColor, 16)+0xFF000000)
133 | .into(bannerImage);
134 | if (getIntent().getBooleanExtra("using_user", false)) {
135 | followButton.setVisibility(View.GONE);
136 | } else {
137 | followButton.setVisibility(View.VISIBLE);
138 | client.userLookup(screenName, new JsonHttpResponseHandler() {
139 | @Override
140 | public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
141 | Log.e(TAG, response.toString());
142 | try {
143 | JSONArray connections = response.getJSONObject(0).getJSONArray("connections");
144 | for (int i = 0; i < connections.length(); i++){
145 | if (connections.get(i).equals("following")) {
146 | user.following = true;
147 | followButton.setBackground(getResources().getDrawable(R.drawable.button_unfollow));
148 | followButton.setTextColor(getResources().getColor(R.color.colorWhite));
149 | followButton.setText("Unfollow");
150 | }
151 | if (connections.get(i).equals("followed_by")) {
152 | tvFollowsYou.setVisibility(View.VISIBLE);
153 | }
154 | }
155 | } catch (JSONException e) {
156 | e.printStackTrace();
157 | Log.e(TAG, String.format("Error occured while parsing json in unfollow/follow"));
158 | }
159 | }
160 |
161 | @Override
162 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
163 | Log.e(TAG, errorResponse.toString());
164 | }
165 | @Override
166 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
167 | Log.e(TAG, errorResponse.toString());
168 | }
169 | @Override
170 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
171 | Log.e(TAG, responseString);
172 | }
173 | });
174 | }
175 | followButton.setOnClickListener(new View.OnClickListener() {
176 | @Override
177 | public void onClick(View v) {
178 | // toggle to button to not following (to false) the person (thus to "follow") if we follow the person
179 | // toggle to button to following (to true) the person (thus to "unfollow") if we don't follow the person
180 | toggleFollowButton(!user.following);
181 |
182 | // true means we follow the user and want to unfollow them,
183 | // changes the status of following for the user if good
184 | followToggle(user.following, screenName, user);
185 | }
186 | });
187 |
188 | }
189 |
190 | public void toggleFollowButton(boolean toFollowing) {
191 | if (toFollowing) {
192 | followButton.setBackground(getResources().getDrawable(R.drawable.button_unfollow));
193 | followButton.setTextColor(getResources().getColor(R.color.colorWhite));
194 | followButton.setText("Unfollow");
195 | } else {
196 | followButton.setBackground(getResources().getDrawable(R.drawable.button_follow));
197 | followButton.setTextColor(getResources().getColor(R.color.colorAccent));
198 | followButton.setText("Follow");
199 | }
200 | }
201 |
202 | public void followToggle(final boolean followsUser, String screenName, final User user) {
203 | final String text = followsUser ? "unfollowing" : "following";
204 | client.followToggle(screenName, followsUser, new JsonHttpResponseHandler() {
205 | @Override
206 | public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
207 | super.onSuccess(statusCode, headers, response);
208 | Toast.makeText(getBaseContext(), String.format("Success %s", text), Toast.LENGTH_SHORT).show();
209 | // if it worked, change the status of user to the opposite of what it used to be
210 | user.following = !followsUser;
211 | }
212 |
213 | @Override
214 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
215 | Log.e(client.TAG, String.format("Failure %s: %s", text, errorResponse.toString()));
216 | throwable.printStackTrace();
217 | Toast.makeText(getBaseContext(), String.format("Failure %s", text), Toast.LENGTH_SHORT).show();
218 | toggleFollowButton(followsUser);
219 | }
220 |
221 | @Override
222 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
223 | Log.e(client.TAG, String.format("Failure %s: %s", text, errorResponse.toString()));
224 | throwable.printStackTrace();
225 | Toast.makeText(getBaseContext(), String.format("Failure %s", text), Toast.LENGTH_SHORT).show();
226 | toggleFollowButton(followsUser);
227 | }
228 |
229 | @Override
230 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
231 | Log.e(client.TAG, String.format("Failure %s: %s", text, responseString));
232 | throwable.printStackTrace();
233 | Toast.makeText(getBaseContext(), String.format("Failure %s", text), Toast.LENGTH_SHORT).show();
234 | toggleFollowButton(followsUser);
235 | }
236 | });
237 | }
238 |
239 | public void onError(int code, String message) {
240 | String onErrorString = "onError error occurred";
241 | switch (code) {
242 | case 0: {
243 | // error while parsing the userId
244 | Toast.makeText(getBaseContext(), String.format("Error occurred while parsing the userId. %s", message), Toast.LENGTH_LONG).show();
245 | break;
246 | }
247 | case 1: {
248 | // error while parsing the json from twitter server response
249 | Toast.makeText(getBaseContext(), String.format("Error occurred while while parsing the json from the twitter server response. %s", message), Toast.LENGTH_LONG).show();
250 | break;
251 | }
252 | case 2: {
253 | // error while sending a request to twitter server
254 | Toast.makeText(getBaseContext(), String.format("Error occurred while while sending a request to the twitter server. %s", message), Toast.LENGTH_LONG).show();
255 | break;
256 | }
257 | default: {
258 | // unidentified error
259 | Toast.makeText(getBaseContext(), String.format("Unidentified error occured."), Toast.LENGTH_LONG).show();
260 | break;
261 | }
262 | }
263 | Log.e(TAG, String.format("%s at %s: %s", onErrorString, code, message));
264 | finish(); // finish because bad error
265 | }
266 |
267 |
268 | // Toast statement skeleton
269 | // Toast.makeText(getBaseContext(), String.format("%s"), Toast.LENGTH_LONG).show();
270 | }
271 |
272 |
273 |
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/fragments/HomeTimelineFragment.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate.fragments;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v4.widget.SwipeRefreshLayout;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.util.Log;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.Toast;
12 |
13 | import com.codepath.apps.restclienttemplate.R;
14 | import com.codepath.apps.restclienttemplate.TwitterApp;
15 | import com.codepath.apps.restclienttemplate.TwitterClient;
16 | import com.codepath.apps.restclienttemplate.models.Tweet;
17 | import com.loopj.android.http.JsonHttpResponseHandler;
18 |
19 | import org.json.JSONArray;
20 | import org.json.JSONObject;
21 |
22 | import cz.msebera.android.httpclient.Header;
23 |
24 | /**
25 | * Created by robertvunabandi on 7/3/17.
26 | */
27 |
28 | public class HomeTimelineFragment extends TweetsListFragment {
29 | private TwitterClient client;
30 | int totalTweets = 10;
31 |
32 | @Override
33 | public void onCreate(@Nullable Bundle savedInstanceState) {
34 | super.onCreate(savedInstanceState);
35 |
36 | client = TwitterApp.getRestClient();
37 | populateTimeline(totalTweets);
38 | }
39 |
40 | @Nullable
41 | @Override
42 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
43 | // return super.onCreateView(inflater, container, savedInstanceState);
44 | View v = inflater.inflate(R.layout.fragments_tweets_list, container, false);
45 | rvTweets = (RecyclerView) v.findViewById(R.id.rvTweet);
46 |
47 | // swipe container
48 | swipeContainer = (SwipeRefreshLayout) v.findViewById(R.id.swipeContainerRefresher);
49 | swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
50 | @Override
51 | public void onRefresh() {
52 | // Toast.makeText(getContext(), "Error occurred. This function is not implemented.", Toast.LENGTH_SHORT).show();
53 | reinitializeTweetsAndAdapter();
54 | populateTimeline(getTweetSize());
55 | swipeContainer.setRefreshing(false);
56 | }
57 | });
58 | swipeContainer.setColorSchemeResources(R.color.colorPrimaryDarkLight, R.color.colorLightGrey, R.color.colorAccent, R.color.colorPrimaryLight);
59 |
60 | // infinite scroll
61 | rvTweets.addOnScrollListener(new RecyclerView.OnScrollListener() {
62 | @Override
63 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
64 | if (dy > 0) {
65 | int lastPosition = layoutManager.findLastVisibleItemPosition();
66 | if (lastPosition > tweets.size() - 2) {
67 | addToTimeline();
68 | }
69 | }
70 | }
71 | });
72 |
73 | reinitializeTweetsAndAdapter();
74 | return v;
75 |
76 | }
77 |
78 | public void populateTimeline(int count) {
79 | // here, the progress bar is not set to visible because this function is always
80 | // called after reinitializeTweetsAndAdapter(), which makes the progress bar visible
81 | // so we get straight into it with the client.getHomeTimeline
82 |
83 | // set progressbar to visible
84 | client.getHomeTimeline(count, new JsonHttpResponseHandler() {
85 | @Override
86 | public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
87 | // FOR JSON OBJECTS
88 | Log.d(client.TAG, response.toString());
89 | }
90 | @Override
91 | public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
92 | // iterate through the JSON array, for each entry deserialize the JSON Object
93 | Log.d(TAG, String.format("PopulateTimeline | RESPONSE LENGTH %s SUCCESS at %s", response.length(), HMS.format(CAL.getTime())));
94 | addItems(response, true); // we know we follow the people on our timeline
95 | }
96 | @Override
97 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
98 | Log.e(client.TAG, String.format("REGULAR FAILURE: %s" , responseString));
99 | throwable.printStackTrace();
100 |
101 | Toast.makeText(getContext(), String.format("An error occurred while acquiring the data. Please try again in 2 minutes."), Toast.LENGTH_SHORT).show();
102 | }
103 | @Override
104 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
105 | Log.e(client.TAG, String.format("JSON OBJECT FAILURE: %s" ,errorResponse.toString()));
106 | throwable.printStackTrace();
107 |
108 | Toast.makeText(getContext(), String.format("An error occurred while acquiring the data. Please try again in 2 minutes."), Toast.LENGTH_SHORT).show();
109 | }
110 | @Override
111 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
112 | Log.e(client.TAG, String.format("JSON ARRAY FAILURE: %s" ,errorResponse.toString()));
113 | throwable.printStackTrace();
114 |
115 | Toast.makeText(getContext(), String.format("An error occurred while acquiring the data. Please try again in 2 minutes."), Toast.LENGTH_SHORT).show();
116 | }
117 | });
118 | }
119 |
120 | public void addToTimeline() {
121 | Log.d(TAG, String.format("addToTimeline at %s", HMS.format(CAL.getTime())));
122 |
123 | client.addToTimeline(max_id, new JsonHttpResponseHandler() {
124 | @Override
125 | public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
126 | addItems(response, true); // we know we follow the people on our timeline
127 | totalTweets = getTweetSize(); // changes the number of total tweets
128 | Log.d(TAG, String.format("tweet size: %s, and maxid: %s, PopulateTimeline Home at %s", totalTweets, max_id, HMS.format(CAL.getTime())));
129 | }
130 | @Override
131 | public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
132 | Log.d(concatTag(TAG, client.TAG), response.toString());
133 | }
134 | @Override
135 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
136 | Log.e(concatTag(TAG, client.TAG), errorResponse.toString() + "Damn 1");
137 | throwable.printStackTrace();
138 |
139 | Toast.makeText(getContext(), String.format("An error occurred. Unable to load more tweets."), Toast.LENGTH_SHORT).show();
140 | }
141 | @Override
142 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
143 | Log.e(concatTag(TAG, client.TAG), errorResponse.toString() + "Damn 2");
144 | throwable.printStackTrace();
145 |
146 | Toast.makeText(getContext(), String.format("An error occurred. Unable to load more tweets."), Toast.LENGTH_SHORT).show();
147 | }
148 | @Override
149 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
150 | Log.e(concatTag(TAG, client.TAG), responseString + "Damn 3");
151 | throwable.printStackTrace();
152 |
153 | Toast.makeText(getContext(), String.format("An error occurred. Unable to load more tweets."), Toast.LENGTH_SHORT).show();
154 | }
155 | });
156 | }
157 |
158 | public void addTweet(Tweet tweet){
159 | addItemOne(tweet, 0);
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/fragments/MentionsTimelineFragment.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate.fragments;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v4.widget.SwipeRefreshLayout;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.util.Log;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.Toast;
12 |
13 | import com.codepath.apps.restclienttemplate.R;
14 | import com.codepath.apps.restclienttemplate.TwitterApp;
15 | import com.codepath.apps.restclienttemplate.TwitterClient;
16 | import com.loopj.android.http.JsonHttpResponseHandler;
17 |
18 | import org.json.JSONArray;
19 | import org.json.JSONObject;
20 |
21 | import cz.msebera.android.httpclient.Header;
22 |
23 | /**
24 | * Created by robertvunabandi on 7/3/17.
25 | */
26 |
27 | public class MentionsTimelineFragment extends TweetsListFragment {
28 | // swipe container
29 | private TwitterClient client;
30 | int totalTweets = 15;
31 |
32 | @Override
33 | public void onCreate(@Nullable Bundle savedInstanceState) {
34 | super.onCreate(savedInstanceState);
35 |
36 | client = TwitterApp.getRestClient();
37 | populateTimeline(totalTweets);
38 | }
39 |
40 | @Nullable
41 | @Override
42 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
43 | // return super.onCreateView(inflater, container, savedInstanceState);
44 | View v = inflater.inflate(R.layout.fragments_tweets_list, container, false);
45 | rvTweets = (RecyclerView) v.findViewById(R.id.rvTweet);
46 |
47 | // swipe container
48 | swipeContainer = (SwipeRefreshLayout) v.findViewById(R.id.swipeContainerRefresher);
49 | swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
50 | @Override
51 | public void onRefresh() {
52 | // TODO - Implement this functions
53 | // Toast.makeText(getContext(), "Error occurred. This function is not implemented.", Toast.LENGTH_SHORT).show();
54 | reinitializeTweetsAndAdapter();
55 | populateTimeline(getTweetSize());
56 | swipeContainer.setRefreshing(false);
57 | }
58 | });
59 | swipeContainer.setColorSchemeResources(R.color.colorPrimaryDarkLight, R.color.colorLightGrey, R.color.colorAccent, R.color.colorPrimaryLight);
60 |
61 | // infinite scroll
62 | rvTweets.addOnScrollListener(new RecyclerView.OnScrollListener() {
63 | @Override
64 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
65 | if (dy > 0) {
66 | int lastPosition = layoutManager.findLastVisibleItemPosition();
67 | if (lastPosition > tweets.size() - 3) {
68 | addToTimeline();
69 | }
70 | }
71 | }
72 | });
73 |
74 | reinitializeTweetsAndAdapter();
75 | return v;
76 |
77 | }
78 |
79 | public void populateTimeline(int count) {
80 | // here, the progress bar is not set to visible because this function is always
81 | // called after reinitializeTweetsAndAdapter(), which makes the progress bar visible
82 | // so we get straight into it with the client.getHomeTimeline
83 |
84 | client.getMentionsTimeline(count, new JsonHttpResponseHandler() {
85 | @Override
86 | public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
87 | // FOR JSON OBJECTS
88 | Log.d(client.TAG, response.toString());
89 | }
90 | @Override
91 | public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
92 | // iterate through the JSON array, for each entry deserialize the JSON Object
93 | Log.d(TAG, String.format("PopulateTimeline | RESPONSE LENGTH %s SUCCESS at %s", response.length(), HMS.format(CAL.getTime())));
94 | addItems(response, false); // we don't know if we follow these people or not
95 | }
96 | @Override
97 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
98 | Log.e(client.TAG, String.format("REGULAR FAILURE: %s" , responseString));
99 | throwable.printStackTrace();
100 |
101 | Toast.makeText(getContext(), String.format("An error occurred while acquiring the data. Please try again in 2 minutes."), Toast.LENGTH_LONG).show();
102 | }
103 | @Override
104 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
105 | Log.e(client.TAG, String.format("JSON OBJECT FAILURE: %s" ,errorResponse.toString()));
106 | throwable.printStackTrace();
107 |
108 | Toast.makeText(getContext(), String.format("An error occurred while acquiring the data. Please try again in 2 minutes."), Toast.LENGTH_LONG).show();
109 | }
110 | @Override
111 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
112 | Log.e(client.TAG, String.format("JSON ARRAY FAILURE: %s" ,errorResponse.toString()));
113 | throwable.printStackTrace();
114 |
115 | Toast.makeText(getContext(), String.format("An error occurred while acquiring the data. Please try again in 2 minutes."), Toast.LENGTH_LONG).show();
116 | }
117 | });
118 | }
119 |
120 | public void addToTimeline() {
121 | Log.d(TAG, String.format("addToTimeline at %s", HMS.format(CAL.getTime())));
122 |
123 | client.addToMentionsTimeline(max_id, new JsonHttpResponseHandler() {
124 | @Override
125 | public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
126 | addItems(response, false); // we don't know if we follow these people or not
127 | totalTweets = getTweetSize(); // changes the number of total tweets
128 | Log.d(TAG, String.format("tweet size: %s, and maxid: %s, PopulateTimelin Mentions at %s", totalTweets, max_id, HMS.format(CAL.getTime())));
129 | }
130 | @Override
131 | public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
132 | Log.d(client.TAG, response.toString());
133 | }
134 | @Override
135 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
136 | Log.e(client.TAG, errorResponse.toString());
137 | throwable.printStackTrace();
138 |
139 | Toast.makeText(getContext(), String.format("An error occurred while acquiring the data. Please try again in 15 minutes."), Toast.LENGTH_LONG).show();
140 | }
141 | @Override
142 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
143 | Log.e(client.TAG, errorResponse.toString());
144 | throwable.printStackTrace();
145 |
146 | Toast.makeText(getContext(), String.format("An error occurred while acquiring the data. Please try again in 15 minutes."), Toast.LENGTH_LONG).show();
147 | }
148 | @Override
149 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
150 | Log.e(client.TAG, responseString);
151 | throwable.printStackTrace();
152 |
153 | Toast.makeText(getContext(), String.format("An error occurred while acquiring the data. Please try again in 15 minutes."), Toast.LENGTH_LONG).show();
154 | }
155 | });
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/fragments/TweetsListFragment.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate.fragments;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v4.app.Fragment;
6 | import android.support.v4.widget.SwipeRefreshLayout;
7 | import android.support.v7.widget.LinearLayoutManager;
8 | import android.support.v7.widget.RecyclerView;
9 | import android.util.Log;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 |
14 | import com.codepath.apps.restclienttemplate.R;
15 | import com.codepath.apps.restclienttemplate.TweetAdapter;
16 | import com.codepath.apps.restclienttemplate.models.Tweet;
17 |
18 | import org.json.JSONArray;
19 | import org.json.JSONException;
20 |
21 | import java.text.SimpleDateFormat;
22 | import java.util.ArrayList;
23 | import java.util.Calendar;
24 |
25 | /**
26 | * Created by robertvunabandi on 7/3/17.
27 | */
28 |
29 | public class TweetsListFragment extends Fragment {
30 |
31 | public final int REQUEST_CODE = 200;
32 | public static final String TAG = "TweetsListFragment";
33 | public long max_id = Long.MAX_VALUE; // lowest id
34 |
35 | // for swiping to refresh tweets
36 | public SwipeRefreshLayout swipeContainer;
37 |
38 | // declare important variables
39 | TweetAdapter tweetAdapter;
40 | ArrayList tweets;
41 | public RecyclerView rvTweets;
42 |
43 | private HomeTimelineFragment timelineFragment;
44 | private MentionsTimelineFragment mentionFragment;
45 |
46 | // the linear layout
47 | LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
48 |
49 | // Timer
50 | Calendar CAL = Calendar.getInstance();
51 | SimpleDateFormat HMS = new SimpleDateFormat("HH:mm:ss");
52 |
53 | // inflation happens inside onCreateView
54 | @Nullable
55 | @Override
56 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
57 | // inflate layout
58 | View v = inflater.inflate(R.layout.fragments_tweets_list, container, false);
59 | rvTweets = (RecyclerView) v.findViewById(R.id.rvTweet);
60 |
61 | // swipe container
62 | swipeContainer = (SwipeRefreshLayout) v.findViewById(R.id.swipeContainerRefresher);
63 | swipeContainer.setColorSchemeResources(R.color.colorPrimaryDarkLight, R.color.colorLightGrey, R.color.colorAccent, R.color.colorPrimaryLight);
64 |
65 | reinitializeTweetsAndAdapter();
66 | return v;
67 | }
68 |
69 | public void reinitializeTweetsAndAdapter(){
70 | tweets = new ArrayList<>(); // init the array list (data source)
71 | tweetAdapter = new TweetAdapter(tweets); // construct adapter from data source
72 | // tweetAdapter.max_id = max_id_from_timeline; // TEMP
73 | rvTweets.setLayoutManager(layoutManager); // RecyclerView setup (layout manager, use adapter)
74 | rvTweets.setAdapter(tweetAdapter); // set the Adapter
75 |
76 | Log.d(TAG, String.format("Tweets reinitialized at %s", HMS.format(CAL.getTime())));
77 | }
78 |
79 | public void addItems(JSONArray response, boolean following){
80 | for (int i = 0; i < response.length(); i++){
81 | // convert each object into a tweet model inserted in the following tweet object
82 | Tweet tweet;
83 | try {
84 | tweet = Tweet.fromJSON(response.getJSONObject(i));
85 | tweet.user.following = following;
86 | // Log.d(TAG, String.format("New tweet at index %s of response on addItems. MAXID: %s, Tweet's id: %s", i, max_id, tweet.uid));
87 | // add the tweet model to our data source
88 | tweets.add(tweet);
89 | // notify the adapter that we've added an item
90 | tweetAdapter.notifyItemInserted(tweets.size() - 1);
91 | // bc we start from 0, the total number of tweets added to our array list will be the size of tweets - 1
92 |
93 | // update the max id of this adapter
94 | max_id = tweet.uid < max_id ? tweet.uid : max_id;
95 | Log.d(TAG, String.format("PopulateTimeline | Tweet at index %s SUCCESS at %s", i, HMS.format(CAL.getTime())));
96 | } catch (JSONException e) {
97 | Log.d(TAG, String.format("PopulateTimeline | Tweet at index %s FAILED at %s", i, HMS.format(CAL.getTime())));
98 | e.printStackTrace();
99 | }
100 |
101 | }
102 | Log.d(TAG, String.format("PopulateTimeline SUCCESS at %s", HMS.format(CAL.getTime())));
103 | }
104 |
105 | public void addItemOne(Tweet tweet, int position) {
106 | tweets.add(position, tweet);
107 | tweetAdapter.notifyItemInserted(position);
108 | // scrolls back to position 0 to see the tweet
109 | rvTweets.scrollToPosition(position);
110 | }
111 |
112 | public long getMaxId() {
113 | return max_id - 1;
114 | }
115 | public int getTweetSize() {
116 | return tweets.size();
117 | }
118 |
119 | public String concatTag(String TAG1, String TAG2) {
120 | return TAG1 + ": " + TAG2;
121 | }
122 |
123 | private HomeTimelineFragment getTimelineFragment() {
124 | if (timelineFragment == null) {
125 | timelineFragment = new HomeTimelineFragment();
126 | }
127 | return timelineFragment;
128 | }
129 |
130 | private MentionsTimelineFragment getMentionFragment() {
131 | if (timelineFragment == null) {
132 | mentionFragment = new MentionsTimelineFragment();
133 | }
134 | return mentionFragment;
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/fragments/TweetsPagerAdapter.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate.fragments;
2 |
3 | import android.content.Context;
4 | import android.support.v4.app.Fragment;
5 | import android.support.v4.app.FragmentManager;
6 | import android.support.v4.app.FragmentPagerAdapter;
7 |
8 | /**
9 | * Created by robertvunabandi on 7/3/17.
10 | */
11 |
12 | public class TweetsPagerAdapter extends FragmentPagerAdapter {
13 |
14 | // TODO - Add a search fragment
15 | private String[] tabTitles = new String[]{"Home", "Mentions"};
16 | private Context context;
17 |
18 | private HomeTimelineFragment timelineFragment;
19 | private MentionsTimelineFragment mentionsFragment;
20 |
21 | public TweetsPagerAdapter(FragmentManager fm, Context context) {
22 | super(fm);
23 | timelineFragment = new HomeTimelineFragment();
24 | mentionsFragment = new MentionsTimelineFragment();
25 | this.context = context;
26 | }
27 |
28 | // return the total number of fragments there are (similar to getCount from RecyclerView
29 | @Override
30 | public int getCount() {
31 | return 2;
32 | }
33 |
34 | // return the fragment to user depending on the position
35 | @Override
36 | public Fragment getItem(int position) {
37 | // if (position == 0) return new HomeTimelineFragment();
38 | // else if (position == 1) return new MentionsTimelineFragment();
39 |
40 | if (position == 0) return timelineFragment;
41 | else if (position == 1) return mentionsFragment;
42 | else return null;
43 | }
44 |
45 | // return the fragment title
46 | @Override
47 | public CharSequence getPageTitle(int position) {
48 | return tabTitles[position];
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/fragments/UserTimelineFragment.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate.fragments;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v4.widget.SwipeRefreshLayout;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.util.Log;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.Toast;
12 |
13 | import com.codepath.apps.restclienttemplate.R;
14 | import com.codepath.apps.restclienttemplate.TwitterApp;
15 | import com.codepath.apps.restclienttemplate.TwitterClient;
16 | import com.loopj.android.http.JsonHttpResponseHandler;
17 |
18 | import org.json.JSONArray;
19 | import org.json.JSONObject;
20 |
21 | import cz.msebera.android.httpclient.Header;
22 |
23 | /**
24 | * Created by robertvunabandi on 7/4/17.
25 | */
26 |
27 | public class UserTimelineFragment extends TweetsListFragment {
28 | private TwitterClient client;
29 | int totalTweets = 10;
30 |
31 | public static UserTimelineFragment newInstance(String screenName) {
32 | UserTimelineFragment userTimelineFragment = new UserTimelineFragment();
33 | Bundle args = new Bundle();
34 | args.putString("screen_name", screenName);
35 | userTimelineFragment.setArguments(args);
36 | return userTimelineFragment;
37 | }
38 |
39 | @Override
40 | public void onCreate(@Nullable Bundle savedInstanceState) {
41 | super.onCreate(savedInstanceState);
42 |
43 | client = TwitterApp.getRestClient();
44 | populateTimeline(totalTweets);
45 | }
46 |
47 | @Nullable
48 | @Override
49 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
50 | // return super.onCreateView(inflater, container, savedInstanceState);
51 | View v = inflater.inflate(R.layout.fragments_tweets_list, container, false);
52 | rvTweets = (RecyclerView) v.findViewById(R.id.rvTweet);
53 |
54 | // swipe container
55 | swipeContainer = (SwipeRefreshLayout) v.findViewById(R.id.swipeContainerRefresher);
56 | swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
57 | @Override
58 | public void onRefresh() {
59 | // Toast.makeText(getContext(), "Error occurred. This function is not implemented.", Toast.LENGTH_SHORT).show();
60 | reinitializeTweetsAndAdapter();
61 | populateTimeline(getTweetSize());
62 | swipeContainer.setRefreshing(false);
63 | }
64 | });
65 | swipeContainer.setColorSchemeResources(R.color.colorPrimaryDarkLight, R.color.colorLightGrey, R.color.colorAccent, R.color.colorPrimaryLight);
66 |
67 | // infinite scroll
68 | rvTweets.addOnScrollListener(new RecyclerView.OnScrollListener() {
69 | @Override
70 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
71 | if (dy > 0) {
72 | int lastPosition = layoutManager.findLastVisibleItemPosition();
73 | if (lastPosition > tweets.size() - 2) {
74 | addToTimeline();
75 | }
76 | }
77 | }
78 | });
79 |
80 | reinitializeTweetsAndAdapter();
81 | return v;
82 |
83 | }
84 |
85 | public void populateTimeline(int count) {
86 | // here, the progress bar is not set to visible because this function is always
87 | // called after reinitializeTweetsAndAdapter(), which makes the progress bar visible
88 | // so we get straight into it with the client.getHomeTimeline
89 |
90 | // set progressbar to visible
91 | client.getUserTimeline(getArguments().getString("screen_name"), count, new JsonHttpResponseHandler() {
92 | @Override
93 | public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
94 | // FOR JSON OBJECTS
95 | Log.d(client.TAG, response.toString());
96 | }
97 | @Override
98 | public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
99 | // iterate through the JSON array, for each entry deserialize the JSON Object
100 | Log.d(TAG, String.format("PopulateTimeline | RESPONSE LENGTH %s SUCCESS at %s", response.length(), HMS.format(CAL.getTime())));
101 | addItems(response, false); // we don't know follow ourselves
102 | }
103 | @Override
104 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
105 | Log.e(client.TAG, String.format("REGULAR FAILURE: %s" , responseString));
106 | throwable.printStackTrace();
107 |
108 | Toast.makeText(getContext(), String.format("An error occurred while acquiring the data. Please try again in 2 minutes."), Toast.LENGTH_LONG).show();
109 | }
110 | @Override
111 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
112 | Log.e(client.TAG, String.format("JSON OBJECT FAILURE: %s" ,errorResponse.toString()));
113 | throwable.printStackTrace();
114 |
115 | Toast.makeText(getContext(), String.format("An error occurred while acquiring the data. Please try again in 2 minutes."), Toast.LENGTH_LONG).show();
116 | }
117 | @Override
118 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
119 | Log.e(client.TAG, String.format("JSON ARRAY FAILURE: %s" ,errorResponse.toString()));
120 | throwable.printStackTrace();
121 |
122 | Toast.makeText(getContext(), String.format("An error occurred while acquiring the data. Please try again in 2 minutes."), Toast.LENGTH_LONG).show();
123 | }
124 | });
125 | }
126 |
127 | public void addToTimeline() {
128 | Log.d(TAG, String.format("addToTimeline at %s", HMS.format(CAL.getTime())));
129 |
130 | client.addToUserTimeline(getArguments().getString("screen_name"), max_id, new JsonHttpResponseHandler() {
131 | @Override
132 | public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
133 | addItems(response, false); // we don't know follow ourselves
134 | totalTweets = getTweetSize(); // changes the number of total tweets
135 | Log.d(TAG, String.format("PopulateTimeline SUCCESS at %s", HMS.format(CAL.getTime())));
136 | Log.d(TAG, String.format("tweet size: %s, and maxid: %s, PopulateTimeline User at %s", totalTweets, max_id, HMS.format(CAL.getTime())));
137 | }
138 | @Override
139 | public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
140 | Log.d(client.TAG, response.toString());
141 | }
142 | @Override
143 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
144 | Log.e(client.TAG, errorResponse.toString() + "Damn 1");
145 | throwable.printStackTrace();
146 |
147 | Toast.makeText(getContext(), String.format("An error occurred. Unable to load more tweets."), Toast.LENGTH_SHORT).show();
148 | }
149 | @Override
150 | public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
151 | Log.e(client.TAG, errorResponse.toString() + "Damn 2");
152 | throwable.printStackTrace();
153 |
154 | Toast.makeText(getContext(), String.format("An error occurred. Unable to load more tweets."), Toast.LENGTH_SHORT).show();
155 | }
156 | @Override
157 | public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
158 | Log.e(client.TAG, responseString + "Damn 3");
159 | throwable.printStackTrace();
160 |
161 | Toast.makeText(getContext(), String.format("An error occurred. Unable to load more tweets."), Toast.LENGTH_SHORT).show();
162 | }
163 | });
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/models/Media.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate.models;
2 |
3 | import org.json.JSONArray;
4 | import org.json.JSONException;
5 | import org.json.JSONObject;
6 | import org.parceler.Parcel;
7 |
8 | /**
9 | * Created by robertvunabandi on 7/1/17.
10 | */
11 |
12 | @Parcel
13 | public class Media {
14 | public String url, urlHTTPS;
15 | public long uid;
16 |
17 | public Media() {}
18 |
19 | public static Media fromJSON(JSONArray jsonObject) throws JSONException {
20 | Media media = new Media();
21 | JSONObject MED = jsonObject.getJSONObject(0);
22 | // extract the values
23 | media.url = MED.getString("media_url");
24 | media.urlHTTPS = MED.getString("media_url_https");
25 | media.uid = MED.getLong("id");
26 |
27 | return media;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/models/SampleModel.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate.models;
2 |
3 | import com.codepath.apps.restclienttemplate.MyDatabase;
4 | import com.raizlabs.android.dbflow.annotation.Column;
5 | import com.raizlabs.android.dbflow.annotation.PrimaryKey;
6 | import com.raizlabs.android.dbflow.annotation.Table;
7 | import com.raizlabs.android.dbflow.sql.language.Select;
8 | import com.raizlabs.android.dbflow.structure.BaseModel;
9 |
10 | import org.json.JSONException;
11 | import org.json.JSONObject;
12 |
13 | import java.util.List;
14 |
15 | /*
16 | * This is a temporary, sample model that demonstrates the basic structure
17 | * of a SQLite persisted Model object. Check out the DBFlow wiki for more details:
18 | * https://github.com/codepath/android_guides/wiki/DBFlow-Guide
19 | *
20 | * Note: All models **must extend from** `BaseModel` as shown below.
21 | *
22 | */
23 | @Table(database = MyDatabase.class)
24 | public class SampleModel extends BaseModel {
25 |
26 | @PrimaryKey
27 | @Column
28 | Long id;
29 |
30 | // Define table fields
31 | @Column
32 | private String name;
33 |
34 | public SampleModel() {
35 | super();
36 | }
37 |
38 | // Parse model from JSON
39 | public SampleModel(JSONObject object){
40 | super();
41 |
42 | try {
43 | this.name = object.getString("title");
44 | } catch (JSONException e) {
45 | e.printStackTrace();
46 | }
47 | }
48 |
49 | // Getters
50 | public String getName() {
51 | return name;
52 | }
53 |
54 | // Setters
55 | public void setName(String name) {
56 | this.name = name;
57 | }
58 |
59 | /* The where class in this code below will be marked red until you first compile the project, since the code
60 | * for the SampleModel_Table class is generated at compile-time.
61 | */
62 |
63 | // Record Finders
64 | public static SampleModel byId(long id) {
65 | return new Select().from(SampleModel.class).where(SampleModel_Table.id.eq(id)).querySingle();
66 | }
67 |
68 | public static List recentItems() {
69 | return new Select().from(SampleModel.class).orderBy(SampleModel_Table.id, false).limit(300).queryList();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/models/Tweet.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate.models;
2 |
3 | import android.text.format.DateUtils;
4 |
5 | import org.json.JSONException;
6 | import org.json.JSONObject;
7 | import org.parceler.Parcel;
8 |
9 | import java.text.ParseException;
10 | import java.text.SimpleDateFormat;
11 | import java.util.Locale;
12 |
13 | /**
14 | * Created by robertvunabandi on 6/26/17.
15 | */
16 |
17 | @Parcel
18 | public class Tweet {
19 | public String body, createdAt;
20 | public long uid; // DB id for the tweet
21 | public User user;
22 | public Media media;
23 | public int retweetCount, likeCount;
24 | public boolean tweetLiked, tweetRetweeted, mediaFound;
25 |
26 | // deserialize the JSON
27 | public Tweet() {}
28 |
29 | public static Tweet fromJSON(JSONObject jsonObject) throws JSONException{
30 | Tweet tweet = new Tweet();
31 |
32 | // extract the values from JSON
33 | tweet.body = jsonObject.getString("text");
34 | tweet.uid = jsonObject.getLong("id");
35 | tweet.createdAt = getRelativeTimeAgo(jsonObject.getString("created_at"));
36 | // user and media
37 | tweet.user = User.fromJSON(jsonObject.getJSONObject("user"));
38 | try {
39 | tweet.media = Media.fromJSON(jsonObject.getJSONObject("entities").getJSONArray("media"));
40 | tweet.mediaFound = true;
41 | } catch (JSONException e) {
42 | tweet.media = null;
43 | tweet.mediaFound = false;
44 | }
45 |
46 | tweet.retweetCount = jsonObject.getInt("retweet_count");
47 | tweet.likeCount = jsonObject.getInt("favorite_count");
48 | tweet.tweetRetweeted = jsonObject.getBoolean("retweeted");
49 | tweet.tweetLiked = jsonObject.getBoolean("favorited");
50 |
51 | return tweet;
52 | }
53 |
54 | public static String getRelativeTimeAgo(String rawJsonDate) {
55 | String twitterFormat = "EEE MMM dd HH:mm:ss ZZZZZ yyyy";
56 | SimpleDateFormat sf = new SimpleDateFormat(twitterFormat, Locale.ENGLISH);
57 | sf.setLenient(true);
58 |
59 | String relativeDate = "";
60 | try {
61 | long dateMillis = sf.parse(rawJsonDate).getTime();
62 | relativeDate = DateUtils.getRelativeTimeSpanString(dateMillis,
63 | System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS).toString();
64 | } catch (ParseException e) {
65 | e.printStackTrace();
66 | }
67 |
68 | return relativeDate;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/models/TweetText.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate.models;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 |
6 | /**
7 | * Created by robertvunabandi on 6/29/17.
8 | */
9 |
10 | public class TweetText {
11 | public String HTMLFONTOPEN = ""; // color highlight
12 | public String HTMLFONTOPENHASHTAG = ""; // color highlight
13 | public String HTMLFONTCLOSE = "";
14 | public String finalText;
15 |
16 | public TweetText(String text) {
17 | setTweet(text);
18 | }
19 |
20 | public void setTweet(String tweet) {
21 | String[] words = tweet.split("\\s+");
22 | int l = words.length;
23 | for (int i = 0; i < l; i++) {
24 | String replacement = userIntoHTMLBlue(words[i]);
25 | words[i] = replacement;
26 | }
27 | List list = Arrays.asList(words);
28 | finalText = join(list, " ");
29 | }
30 |
31 | public String userIntoHTMLBlue(String userText) {
32 | if (userText.charAt(0) == '@') {
33 | return HTMLFONTOPEN + userText + HTMLFONTCLOSE;
34 | } else if (userText.charAt(0) == '#') {
35 | return HTMLFONTOPENHASHTAG + userText + HTMLFONTCLOSE;
36 | } else {
37 | return userText;
38 | }
39 | }
40 |
41 | public static String join(List list, String delimeter) {
42 | StringBuilder sb = new StringBuilder();
43 | boolean first = true;
44 | for (String item : list) {
45 | if (first) {
46 | first = false;
47 | } else {
48 | sb.append(delimeter);
49 | }
50 | sb.append(item);
51 | }
52 | return sb.toString();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/codepath/apps/restclienttemplate/models/User.java:
--------------------------------------------------------------------------------
1 | package com.codepath.apps.restclienttemplate.models;
2 |
3 | import org.json.JSONException;
4 | import org.json.JSONObject;
5 | import org.parceler.Parcel;
6 |
7 | import static com.codepath.apps.restclienttemplate.models.Tweet.getRelativeTimeAgo;
8 |
9 | /**
10 | * Created by robertvunabandi on 6/26/17.
11 | */
12 |
13 | @Parcel
14 | public class User {
15 | // list attributes
16 | public String name, screenName, profileImageUrl;
17 | public long uid;
18 | // variables for user details
19 | public String profileImageUrlHTTPS, profileBackgroundImageUrl, profileBackgroundImageUrlHTTPS, profileBannerUrl, profileBackgroundColor, createdAt, description;
20 | public long followersCount, followingsCount;
21 | public boolean verifiedUser, followsYou, following;
22 |
23 | public User() {}
24 |
25 | // deserialize the JSON
26 | public static User fromJSON(JSONObject jsonObject) throws JSONException {
27 | User user = new User();
28 |
29 | // extract the values
30 | user.name = jsonObject.getString("name");
31 | user.screenName = jsonObject.getString("screen_name");
32 | user.profileImageUrl = jsonObject.getString("profile_image_url").replace("_normal","");
33 | user.profileImageUrlHTTPS = jsonObject.getString("profile_image_url_https").replace("_normal","");
34 | user.uid = jsonObject.getLong("id");
35 | // for user detail
36 |
37 | user.description = jsonObject.getString("description");
38 | user.profileBackgroundImageUrl = jsonObject.getString("profile_background_image_url").replace("_normal","");
39 | user.profileBackgroundColor = jsonObject.getString("profile_background_color");
40 | user.profileBannerUrl = jsonObject.getString("profile_banner_url");
41 | user.createdAt = getRelativeTimeAgo(jsonObject.getString("created_at")); // when the user was first on twitter
42 | user.followersCount = jsonObject.getLong("followers_count");
43 | user.followingsCount = jsonObject.getLong("friends_count"); // it could be listed_count as well
44 | user.verifiedUser = jsonObject.getBoolean("verified");
45 | user.followsYou = jsonObject.getBoolean("following");
46 | user.following = false; // boolean to modify in case it's true
47 |
48 | return user;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/res/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/.DS_Store
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/.DS_Store
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_face_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_face_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_face_placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_face_placeholder.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_heart_clear_light_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_heart_clear_light_blue.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_heart_clear_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_heart_clear_red.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_heart_solid_light_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_heart_solid_light_blue.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_mode_edit_black_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_mode_edit_black_48dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_person_v1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_person_v1.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_person_v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_person_v2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_person_v3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_person_v3.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_picture_placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_picture_placeholder.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_reply.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_reply.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_reply_accent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_reply_accent.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_reply_black_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_reply_black_48dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_retweet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_retweet.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_retweet_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_retweet_green.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_tweet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_tweet.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_tweet_accent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_tweet_accent.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_tweet_quil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_tweet_quil.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_tweet_quil_accent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-hdpi/ic_tweet_quil_accent.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_face_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_face_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_face_placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_face_placeholder.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_heart_clear_light_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_heart_clear_light_blue.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_heart_clear_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_heart_clear_red.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_heart_solid_light_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_heart_solid_light_blue.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_mode_edit_black_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_mode_edit_black_48dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_person_v1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_person_v1.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_person_v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_person_v2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_person_v3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_person_v3.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_picture_placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_picture_placeholder.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_reply.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_reply.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_reply_accent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_reply_accent.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_reply_black_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_reply_black_48dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_retweet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_retweet.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_retweet_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_retweet_green.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_tweet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_tweet.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_tweet_accent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_tweet_accent.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_tweet_quil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_tweet_quil.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_tweet_quil_accent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-mdpi/ic_tweet_quil_accent.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_face_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_face_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_face_placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_face_placeholder.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_heart_clear_light_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_heart_clear_light_blue.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_heart_clear_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_heart_clear_red.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_heart_solid_light_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_heart_solid_light_blue.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_mode_edit_black_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_mode_edit_black_48dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_person_v1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_person_v1.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_person_v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_person_v2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_person_v3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_person_v3.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_picture_placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_picture_placeholder.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_reply.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_reply.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_reply_accent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_reply_accent.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_reply_black_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_reply_black_48dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_retweet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_retweet.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_retweet_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_retweet_green.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_tweet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_tweet.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_tweet_accent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_tweet_accent.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_tweet_quil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_tweet_quil.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_tweet_quil_accent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xhdpi/ic_tweet_quil_accent.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_face_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_face_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_face_placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_face_placeholder.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_heart_clear_light_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_heart_clear_light_blue.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_heart_clear_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_heart_clear_red.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_heart_solid_light_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_heart_solid_light_blue.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_mode_edit_black_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_mode_edit_black_48dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_person_v1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_person_v1.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_person_v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_person_v2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_person_v3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_person_v3.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_picture_placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_picture_placeholder.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_reply.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_reply.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_reply_accent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_reply_accent.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_reply_black_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_reply_black_48dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_retweet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_retweet.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_retweet_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_retweet_green.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_tweet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_tweet.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_tweet_accent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_tweet_accent.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_tweet_quil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_tweet_quil.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_tweet_quil_accent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxhdpi/ic_tweet_quil_accent.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_face_black_24dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_face_black_24dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_face_placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_face_placeholder.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_heart_clear_light_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_heart_clear_light_blue.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_heart_clear_red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_heart_clear_red.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_heart_solid_light_blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_heart_solid_light_blue.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_mode_edit_black_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_mode_edit_black_48dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_person_v1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_person_v1.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_person_v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_person_v2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_person_v3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_person_v3.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_picture_placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_picture_placeholder.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_reply.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_reply.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_reply_accent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_reply_accent.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_reply_black_48dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_reply_black_48dp.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_retweet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_retweet.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_retweet_green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_retweet_green.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_tweet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_tweet.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_tweet_accent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_tweet_accent.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_tweet_quil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_tweet_quil.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/ic_tweet_quil_accent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable-xxxhdpi/ic_tweet_quil_accent.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/drawable/.DS_Store
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_follow.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | -
12 |
13 |
14 |
15 |
16 |
17 |
18 | -
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/button_unfollow.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 | -
11 |
12 |
13 |
14 |
15 |
16 |
17 | -
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_back_button_accent.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_back_button_light_blue.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_close_accent.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_heart_clear_light_blue_svg.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
10 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_heart_clear_red_svg.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
10 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_heart_solid_light_blue_svg.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
10 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_heart_solid_red_svg.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
10 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_person_v1_svg.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
15 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_person_v2_svg.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
15 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_person_v3_svg.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
15 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_picture_placeholder_svg.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
17 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_reply_accent_svg.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_reply_svg.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_retweet_green_svg.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
10 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_retweet_svg.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
10 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_tweet_accent_svg.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_tweet_quil_accent_svg.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_tweet_quil_svg.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_tweet_svg.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rounded_corners.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_compose.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
18 |
21 |
22 |
32 |
33 |
44 |
45 |
56 |
57 |
58 |
59 |
66 |
67 |
84 |
85 |
102 |
103 |
116 |
117 |
118 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_timeline.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
21 |
22 |
32 |
33 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
56 |
57 |
64 |
65 |
75 |
76 |
90 |
91 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_user_profile.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
21 |
22 |
28 |
29 |
36 |
37 |
49 |
50 |
51 |
52 |
72 |
73 |
74 |
75 |
83 |
84 |
92 |
93 |
103 |
104 |
120 |
121 |
122 |
133 |
134 |
147 |
148 |
149 |
159 |
160 |
163 |
164 |
165 |
172 |
173 |
181 |
182 |
183 |
187 |
188 |
195 |
196 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
216 |
217 |
221 |
222 |
227 |
228 |
229 |
235 |
246 |
247 |
248 |
249 |
258 |
259 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragments_tweets_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_tweet.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
26 |
27 |
34 |
35 |
46 |
47 |
57 |
58 |
59 |
60 |
72 |
73 |
86 |
87 |
100 |
101 |
102 |
103 |
112 |
113 |
117 |
118 |
129 |
130 |
131 |
135 |
136 |
147 |
148 |
155 |
156 |
157 |
161 |
162 |
173 |
174 |
181 |
182 |
183 |
184 |
192 |
193 |
194 |
195 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/login.xml:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_timeline.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-sw600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values-sw720dp-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | 128dp
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/app/src/main/res/values/.DS_Store
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #1c2737
4 | #248ff1
5 | #151e29
6 | #173144
7 | #151e29
8 | #FFF
9 | #0c1217
10 | #05080a
11 | #d4e9fc
12 | #99d4e9fc
13 | #248ff1
14 | #FFffa3c4
15 | #FFd8004e
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 | 90dp
7 | 56dp
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/secrets.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | A8qbuCMT0iOtSZjGwqWavoLnk
4 | uE2fexLkKYa8a04n8E5smykTa3HL1iO0VuOjoGXfzkQ3xdBvgm
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Twitter
5 | Log in
6 | oauth
7 | cprest
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
18 |
22 |
23 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | jcenter()
5 | }
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:2.3.3'
8 | }
9 | }
10 |
11 | allprojects {
12 | repositories {
13 | jcenter()
14 | maven { url "https://jitpack.io" }
15 | maven { url "https://maven.google.com" }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/gradle/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/robertvunabandi/twitter/e19052ff5d6e86aaa273bcd98e76fa7d291d021a/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Apr 22 00:39:08 PDT 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/success.md:
--------------------------------------------------------------------------------
1 | ## OAUTH CALLBACK SUCCESSFUL
2 |
3 | If you see this page, the OAuth callback succeeded. Because you are possibly using Android 7.0+, Twitter's redirect may not be working correctly.
4 |
5 | Switch back to your Android app and try to login again!
6 |
7 | If you are using the default callback URL provided in this project, clicking this [oauth://cprest](oauth://cprest) link should close the app.
8 |
9 | For more details, see:
10 |
11 | * [https://twittercommunity.com/t/implementing-web-based-oauth-flow-with-twitter-api-breaks-for-android-nougat-api-25/86821](https://twittercommunity.com/t/implementing-web-based-oauth-flow-with-twitter-api-breaks-for-android-nougat-api-25/86821)
12 | * [https://developer.chrome.com/multidevice/android/intents](https://developer.chrome.com/multidevice/android/intents)
--------------------------------------------------------------------------------