├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── naman14 │ │ └── hacktoberfest │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── ArefRuqaa-Bold.ttf │ │ └── ArefRuqaa-Regular.ttf │ ├── ic_launcher-web.png │ ├── java │ │ └── com │ │ │ └── naman14 │ │ │ └── hacktoberfest │ │ │ ├── MainActivity.java │ │ │ ├── adapters │ │ │ ├── PRAdapter.java │ │ │ └── ProjectsAdapter.java │ │ │ ├── fragment │ │ │ ├── AboutFragment.java │ │ │ ├── ExploreFragment.java │ │ │ └── StatusFragment.java │ │ │ ├── network │ │ │ ├── api │ │ │ │ ├── ApiEndPoints.java │ │ │ │ ├── ApiInterceptor.java │ │ │ │ ├── BaseURL.java │ │ │ │ ├── GithubApiManager.java │ │ │ │ └── services │ │ │ │ │ └── SearchService.java │ │ │ ├── entity │ │ │ │ ├── Issue.java │ │ │ │ ├── Label.java │ │ │ │ ├── SearchResponse.java │ │ │ │ └── User.java │ │ │ └── repository │ │ │ │ └── GithubRepository.java │ │ │ ├── utils │ │ │ ├── AnimUtils.java │ │ │ ├── FabAnimationUtils.java │ │ │ └── Utils.java │ │ │ └── widgets │ │ │ ├── CircleImageView.java │ │ │ ├── FlowLayout.java │ │ │ ├── GridRecyclerView.java │ │ │ └── TypefaceTextView.java │ └── res │ │ ├── anim │ │ ├── design_fab_out.xml │ │ ├── grid_layout_animation.xml │ │ └── slide_in_bottom.xml │ │ ├── animator │ │ └── raise.xml │ │ ├── color │ │ └── bottom_navigation_colors.xml │ │ ├── drawable-anydpi │ │ ├── compass_outline.xml │ │ ├── ic_arrow_forward.xml │ │ ├── ic_av_timer.xml │ │ ├── ic_code.xml │ │ ├── ic_done.xml │ │ └── ic_info_outline.xml │ │ ├── drawable │ │ ├── edittext_bg.xml │ │ ├── filter.png │ │ ├── frame_fab_light.xml │ │ ├── git_pull_merged.png │ │ ├── git_pull_open.png │ │ ├── ic_arrow_drop_down.xml │ │ ├── ic_arrow_forward_white.xml │ │ ├── issue_number_bg.xml │ │ ├── item_label_bg.xml │ │ └── spinner_bg.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── fragment_about.xml │ │ ├── fragment_explore.xml │ │ ├── fragment_status.xml │ │ ├── item_label.xml │ │ ├── item_project.xml │ │ ├── item_status_pr.xml │ │ └── layout_filter_reveal.xml │ │ ├── menu │ │ └── menu_bottom_navigation.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values-v23 │ │ └── styles.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimen.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── naman14 │ └── hacktoberfest │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── screenshots ├── screenshot1.png ├── screenshot2.png └── screenshot3.png └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | .idea/ 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hacktoberfest-Android 2 | 3 | This Android app allows you to check your Hacktoberfest status and explore issues 4 | and projects to get started with open source community. 5 | 6 | Get it on Google Play 7 | 8 | ## Screenshots 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.compileSdkVersion 5 | defaultConfig { 6 | applicationId "com.naman14.hacktoberfest" 7 | minSdkVersion rootProject.minSdkVersion 8 | targetSdkVersion rootProject.targetSdkVersion 9 | versionCode 6 10 | versionName "3.1" 11 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | 23 | implementation 'androidx.appcompat:appcompat:1.0.0' 24 | implementation 'com.google.android.material:material:1.0.0' 25 | implementation 'androidx.legacy:legacy-support-v13:1.0.0' 26 | implementation 'androidx.cardview:cardview:1.0.0' 27 | implementation 'androidx.browser:browser:1.0.0' 28 | 29 | implementation 'com.afollestad.material-dialogs:core:0.9.4.7' 30 | 31 | implementation "com.jakewharton:butterknife:$rootProject.butterKnifeVersion" 32 | annotationProcessor "com.jakewharton:butterknife-compiler:$rootProject.butterKnifeVersion" 33 | 34 | implementation ("com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion") { 35 | // exclude Retrofit’s OkHttp peer-dependency module and define your own module import 36 | exclude module: 'okhttp' 37 | } 38 | implementation "com.squareup.retrofit2:converter-gson:$rootProject.retrofitVersion" 39 | implementation "com.squareup.retrofit2:adapter-rxjava:$rootProject.retrofitVersion" 40 | implementation "com.squareup.okhttp3:okhttp:$rootProject.okHttp3Version" 41 | implementation "com.squareup.okhttp3:logging-interceptor:$rootProject.okHttp3Version" 42 | implementation 'com.squareup.picasso:picasso:2.5.2' 43 | 44 | implementation 'io.reactivex:rxandroid:1.2.1' 45 | implementation 'io.reactivex:rxjava:1.3.2' 46 | 47 | testImplementation 'junit:junit:4.12' 48 | } 49 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/naman/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/naman14/hacktoberfest/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.naman14.hacktoberfest; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.naman14.hacktoberfest", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/assets/ArefRuqaa-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naman14/Hacktoberfest-Android/99b368c7b3cc1d2c896eab423304c1b0a4f0c7f8/app/src/main/assets/ArefRuqaa-Bold.ttf -------------------------------------------------------------------------------- /app/src/main/assets/ArefRuqaa-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naman14/Hacktoberfest-Android/99b368c7b3cc1d2c896eab423304c1b0a4f0c7f8/app/src/main/assets/ArefRuqaa-Regular.ttf -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/naman14/Hacktoberfest-Android/99b368c7b3cc1d2c896eab423304c1b0a4f0c7f8/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/naman14/hacktoberfest/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.naman14.hacktoberfest; 2 | 3 | import androidx.annotation.NonNull; 4 | import com.google.android.material.bottomnavigation.BottomNavigationView; 5 | import androidx.fragment.app.Fragment; 6 | import androidx.appcompat.app.AppCompatActivity; 7 | import android.os.Bundle; 8 | import android.view.MenuItem; 9 | 10 | import com.naman14.hacktoberfest.fragment.AboutFragment; 11 | import com.naman14.hacktoberfest.fragment.ExploreFragment; 12 | import com.naman14.hacktoberfest.fragment.StatusFragment; 13 | 14 | import butterknife.BindView; 15 | import butterknife.ButterKnife; 16 | 17 | public class MainActivity extends AppCompatActivity { 18 | 19 | @BindView(R.id.bottomBar) 20 | BottomNavigationView bottomBar; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_main); 26 | ButterKnife.bind(this); 27 | 28 | replaceFragment(new StatusFragment()); 29 | 30 | bottomBar.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { 31 | @Override 32 | public boolean onNavigationItemSelected(@NonNull MenuItem item) { 33 | switch (item.getItemId()) { 34 | case R.id.tab_status: 35 | replaceFragment(new StatusFragment()); 36 | break; 37 | case R.id.tab_explore: 38 | replaceFragment(new ExploreFragment()); 39 | break; 40 | case R.id.tab_about: 41 | replaceFragment(new AboutFragment()); 42 | break; 43 | } 44 | return true; 45 | } 46 | }); 47 | } 48 | 49 | private void replaceFragment(Fragment fragment) { 50 | getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment).commit(); 51 | } 52 | 53 | public BottomNavigationView getBottomBar() { 54 | return bottomBar; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/naman14/hacktoberfest/adapters/PRAdapter.java: -------------------------------------------------------------------------------- 1 | package com.naman14.hacktoberfest.adapters; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.graphics.drawable.Drawable; 6 | import android.graphics.drawable.GradientDrawable; 7 | 8 | import androidx.browser.customtabs.CustomTabsIntent; 9 | import androidx.recyclerview.widget.RecyclerView; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.TextView; 14 | 15 | import com.naman14.hacktoberfest.R; 16 | import com.naman14.hacktoberfest.network.entity.Issue; 17 | import com.naman14.hacktoberfest.utils.Utils; 18 | 19 | import java.util.List; 20 | 21 | import butterknife.BindView; 22 | import butterknife.ButterKnife; 23 | 24 | /** 25 | * Created by naman on 19/9/17. 26 | */ 27 | 28 | public class PRAdapter extends RecyclerView.Adapter { 29 | 30 | private List array; 31 | private Context context; 32 | 33 | public PRAdapter(Context context) { 34 | this.context = context; 35 | } 36 | 37 | @Override 38 | public PRAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 39 | View v = LayoutInflater.from(parent.getContext()).inflate( 40 | R.layout.item_status_pr, parent, false); 41 | return new PRAdapter.ViewHolder(v); 42 | } 43 | 44 | @Override 45 | public void onBindViewHolder(PRAdapter.ViewHolder holder, int position) { 46 | 47 | Issue issue = array.get(position); 48 | 49 | holder.tvPrNumber.setText("#" + issue.getNumber()); 50 | holder.tvPrTitle.setText(issue.getTitle()); 51 | holder.tvPrRepo.setText(getRepoFromUrl(issue.getRepository_url())); 52 | 53 | GradientDrawable drawable = (GradientDrawable) holder.tvPrStatus.getBackground(); 54 | Drawable img; 55 | switch (issue.getState()) { 56 | case "open": 57 | holder.tvPrStatus.setText("Open"); 58 | img = context.getResources().getDrawable( R.drawable.git_pull_open); 59 | img.setBounds( 0, 0, 50, 50 ); 60 | holder.tvPrStatus.setCompoundDrawables(img, null, null, null); 61 | drawable.setColor(Color.parseColor("#2cbe4e")); 62 | break; 63 | // github api doesn't have a way to distinguish between closed and merged state. state is 64 | case "closed": 65 | holder.tvPrStatus.setText("Merged/Closed"); 66 | img = context.getResources().getDrawable( R.drawable.git_pull_merged); 67 | img.setBounds( 0, 0, 50, 50 ); 68 | holder.tvPrStatus.setCompoundDrawables(img, null, null, null); 69 | drawable.setColor(Color.parseColor("#6f42c1")); 70 | break; 71 | } 72 | 73 | } 74 | 75 | @Override 76 | public int getItemCount() { 77 | if (array != null) { 78 | return array.size(); 79 | } else { 80 | return 0; 81 | } 82 | } 83 | 84 | public class ViewHolder extends RecyclerView.ViewHolder { 85 | 86 | @BindView(R.id.tv_pr_number) 87 | TextView tvPrNumber; 88 | 89 | @BindView(R.id.tv_pr_title) 90 | TextView tvPrTitle; 91 | 92 | @BindView(R.id.tv_pr_repo) 93 | TextView tvPrRepo; 94 | 95 | @BindView(R.id.tv_pr_status) 96 | TextView tvPrStatus; 97 | 98 | ViewHolder(View v) { 99 | super(v); 100 | ButterKnife.bind(this, v); 101 | 102 | tvPrNumber.setOnClickListener(new View.OnClickListener() { 103 | @Override 104 | public void onClick(View view) { 105 | Utils.openChromeCustomTab(context, array.get(getAdapterPosition()).getHtml_url()); 106 | } 107 | }); 108 | 109 | v.setOnClickListener(new View.OnClickListener() { 110 | @Override 111 | public void onClick(View view) { 112 | Utils.openChromeCustomTab(context, array.get(getAdapterPosition()).getHtml_url()); 113 | } 114 | }); 115 | 116 | } 117 | } 118 | 119 | private String getRepoFromUrl(String url) { 120 | return url.replace("https://api.github.com/repos/", ""); 121 | } 122 | 123 | public void setData(List data) { 124 | this.array = data; 125 | notifyDataSetChanged(); 126 | } 127 | 128 | public void clearData() { 129 | this.array.clear(); 130 | notifyDataSetChanged(); 131 | } 132 | 133 | public List getData() { 134 | return array; 135 | } 136 | 137 | 138 | } 139 | 140 | -------------------------------------------------------------------------------- /app/src/main/java/com/naman14/hacktoberfest/adapters/ProjectsAdapter.java: -------------------------------------------------------------------------------- 1 | package com.naman14.hacktoberfest.adapters; 2 | 3 | import android.content.Context; 4 | import android.graphics.Color; 5 | import android.graphics.drawable.GradientDrawable; 6 | 7 | import androidx.browser.customtabs.CustomTabsIntent; 8 | import androidx.recyclerview.widget.RecyclerView; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.TextView; 13 | 14 | import com.naman14.hacktoberfest.R; 15 | import com.naman14.hacktoberfest.utils.Utils; 16 | import com.naman14.hacktoberfest.network.entity.Issue; 17 | import com.naman14.hacktoberfest.network.entity.Label; 18 | import com.naman14.hacktoberfest.widgets.FlowLayout; 19 | 20 | import java.util.List; 21 | 22 | import butterknife.BindView; 23 | import butterknife.ButterKnife; 24 | 25 | /** 26 | * Created by naman on 19/9/17. 27 | */ 28 | 29 | public class ProjectsAdapter extends RecyclerView.Adapter { 30 | 31 | private List array; 32 | private Context context; 33 | 34 | private FlowLayout.LayoutParams params; 35 | 36 | public ProjectsAdapter(Context context) { 37 | this.context = context; 38 | this.params = new FlowLayout.LayoutParams(25, 30); 39 | } 40 | 41 | @Override 42 | public ProjectsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 43 | View v = LayoutInflater.from(parent.getContext()).inflate( 44 | R.layout.item_project, parent, false); 45 | return new ProjectsAdapter.ViewHolder(v); 46 | } 47 | 48 | @Override 49 | public void onBindViewHolder(ProjectsAdapter.ViewHolder holder, int position) { 50 | 51 | Issue issue = array.get(position); 52 | 53 | holder.tvIssueNumber.setText("#" + issue.getNumber()); 54 | holder.tvIssueTitle.setText(issue.getTitle()); 55 | holder.tvIssueRepo.setText(getRepoFromUrl(issue.getRepository_url())); 56 | 57 | if (Utils.getLanguagePreference(context).equals("All")) { 58 | holder.tvIssueLanguage.setVisibility(View.GONE); 59 | } else { 60 | holder.tvIssueLanguage.setText(Utils.getLanguagePreference(context)); 61 | } 62 | 63 | if (issue.getLabels() != null && issue.getLabels().size() != 0) { 64 | for (Label label : issue.getLabels()) { 65 | 66 | TextView textView = (TextView) LayoutInflater.from(context).inflate(R.layout.item_label, null); 67 | 68 | GradientDrawable drawable = (GradientDrawable) textView.getBackground(); 69 | drawable.setColor(Color.parseColor("#" + label.getColor())); 70 | 71 | textView.setText(label.getName()); 72 | textView.setTextColor(Utils.getContrastColor(Color.parseColor("#" + label.getColor()))); 73 | 74 | holder.llLabels.addView(textView, params); 75 | } 76 | } 77 | 78 | } 79 | 80 | @Override 81 | public int getItemCount() { 82 | if (array != null) { 83 | return array.size(); 84 | } else { 85 | return 0; 86 | } 87 | } 88 | 89 | public class ViewHolder extends RecyclerView.ViewHolder { 90 | 91 | @BindView(R.id.tv_issue_number) 92 | TextView tvIssueNumber; 93 | 94 | @BindView(R.id.tv_issue_title) 95 | TextView tvIssueTitle; 96 | 97 | @BindView(R.id.tv_issue_repo) 98 | TextView tvIssueRepo; 99 | 100 | @BindView(R.id.tv_issue_language) 101 | TextView tvIssueLanguage; 102 | 103 | @BindView(R.id.ll_labels) 104 | FlowLayout llLabels; 105 | 106 | 107 | ViewHolder(View v) { 108 | super(v); 109 | ButterKnife.bind(this, v); 110 | 111 | 112 | v.setOnClickListener(new View.OnClickListener() { 113 | @Override 114 | public void onClick(View view) { 115 | Utils.openChromeCustomTab(context, array.get(getAdapterPosition()).getHtml_url()); 116 | } 117 | }); 118 | } 119 | } 120 | 121 | private String getRepoFromUrl(String url) { 122 | return url.replace("https://api.github.com/repos/", ""); 123 | } 124 | 125 | public void setData(List data) { 126 | this.array = data; 127 | notifyDataSetChanged(); 128 | } 129 | public void addData(List data) { 130 | int positionStart = array.size() + 1; 131 | this.array.addAll(data); 132 | notifyItemRangeInserted(positionStart,array.size()); 133 | } 134 | public void clearData() { 135 | if (array != null) { 136 | this.array.clear(); 137 | notifyDataSetChanged(); 138 | } 139 | } 140 | 141 | public List getData() { 142 | return array; 143 | } 144 | 145 | } 146 | 147 | -------------------------------------------------------------------------------- /app/src/main/java/com/naman14/hacktoberfest/fragment/AboutFragment.java: -------------------------------------------------------------------------------- 1 | package com.naman14.hacktoberfest.fragment; 2 | 3 | import android.graphics.Paint; 4 | import android.os.Bundle; 5 | import androidx.annotation.Nullable; 6 | import androidx.fragment.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.TextView; 11 | 12 | import com.naman14.hacktoberfest.R; 13 | import com.naman14.hacktoberfest.utils.Utils; 14 | 15 | import butterknife.BindView; 16 | import butterknife.ButterKnife; 17 | import butterknife.OnClick; 18 | 19 | /** 20 | * Created by naman on 4/10/17. 21 | */ 22 | 23 | public class AboutFragment extends Fragment { 24 | 25 | @BindView(R.id.tv_website) 26 | TextView tvWebsite; 27 | 28 | @Nullable 29 | @Override 30 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 31 | View rootView = inflater.inflate(R.layout.fragment_about, container, false); 32 | 33 | ButterKnife.bind(this, rootView); 34 | 35 | tvWebsite.setPaintFlags(Paint.UNDERLINE_TEXT_FLAG); 36 | 37 | return rootView; 38 | } 39 | 40 | @OnClick(R.id.tv_website) 41 | public void websiteClicked() { 42 | Utils.openChromeCustomTab(getActivity(), "https://hacktoberfest.digitalocean.com"); 43 | } 44 | 45 | @OnClick(R.id.tv_view_github) 46 | public void viewGithub() { 47 | Utils.openChromeCustomTab(getActivity(), "https://github.com/naman14/Hacktoberfest-Android"); 48 | } 49 | 50 | @OnClick(R.id.tv_sign_up) 51 | public void signUp() { 52 | Utils.openChromeCustomTab(getActivity(), "https://hacktoberfest.digitalocean.com/sign_up/register"); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/com/naman14/hacktoberfest/fragment/ExploreFragment.java: -------------------------------------------------------------------------------- 1 | package com.naman14.hacktoberfest.fragment; 2 | 3 | import android.animation.Animator; 4 | import android.animation.AnimatorListenerAdapter; 5 | import android.animation.AnimatorSet; 6 | import android.animation.ObjectAnimator; 7 | import android.graphics.Color; 8 | import android.os.Bundle; 9 | import androidx.annotation.Nullable; 10 | import com.google.android.material.floatingactionbutton.FloatingActionButton; 11 | import androidx.fragment.app.Fragment; 12 | import androidx.core.content.ContextCompat; 13 | import androidx.core.widget.NestedScrollView; 14 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; 15 | import androidx.recyclerview.widget.LinearLayoutManager; 16 | import androidx.recyclerview.widget.RecyclerView; 17 | import android.view.LayoutInflater; 18 | import android.view.View; 19 | import android.view.ViewAnimationUtils; 20 | import android.view.ViewGroup; 21 | import android.view.ViewTreeObserver; 22 | import android.widget.Button; 23 | import android.widget.ProgressBar; 24 | import android.widget.TextView; 25 | import android.widget.Toast; 26 | 27 | import com.afollestad.materialdialogs.MaterialDialog; 28 | import com.naman14.hacktoberfest.R; 29 | import com.naman14.hacktoberfest.adapters.ProjectsAdapter; 30 | import com.naman14.hacktoberfest.network.entity.Issue; 31 | import com.naman14.hacktoberfest.network.repository.GithubRepository; 32 | import com.naman14.hacktoberfest.utils.AnimUtils; 33 | import com.naman14.hacktoberfest.utils.FabAnimationUtils; 34 | import com.naman14.hacktoberfest.utils.Utils; 35 | 36 | import java.util.Arrays; 37 | import java.util.List; 38 | 39 | import butterknife.BindView; 40 | import butterknife.ButterKnife; 41 | import butterknife.OnClick; 42 | import rx.Subscriber; 43 | import rx.android.schedulers.AndroidSchedulers; 44 | import rx.schedulers.Schedulers; 45 | 46 | /** 47 | * Created by naman on 4/10/17. 48 | */ 49 | 50 | public class ExploreFragment extends Fragment { 51 | 52 | @BindView(R.id.recyclerView) 53 | RecyclerView recyclerView; 54 | 55 | @BindView(R.id.progressBar) 56 | ProgressBar progressBar; 57 | 58 | @BindView(R.id.fab) 59 | FloatingActionButton fab; 60 | 61 | @BindView(R.id.confirm_save_container) 62 | ViewGroup confirmSaveContainer; 63 | 64 | @BindView(R.id.results_scrim) 65 | View resultsScrim; 66 | 67 | @BindView(R.id.save_confirmed) 68 | Button saveConfirmed; 69 | 70 | @BindView(R.id.tv_language) 71 | TextView tvLanguage; 72 | 73 | @BindView(R.id.tv_tags) 74 | TextView tvTags; 75 | 76 | @BindView(R.id.scrollView) 77 | NestedScrollView scrollView; 78 | 79 | @BindView(R.id.swipe_refresh_layout) 80 | SwipeRefreshLayout swipeRefreshLayout; 81 | 82 | private ProjectsAdapter adapter; 83 | private int page = 1; 84 | private Boolean loading = false; 85 | 86 | @Nullable 87 | @Override 88 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 89 | View rootView = inflater.inflate(R.layout.fragment_explore, container, false); 90 | 91 | ButterKnife.bind(this, rootView); 92 | 93 | setupFilter(); 94 | setupRecyclerview(); 95 | 96 | setupSwipeRefreshLayout(); 97 | 98 | fetchIssues(); 99 | return rootView; 100 | } 101 | 102 | private void setupSwipeRefreshLayout() { 103 | 104 | swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { 105 | @Override 106 | public void onRefresh() { 107 | page=1; 108 | fetchIssues(); 109 | } 110 | }); 111 | } 112 | 113 | 114 | private void setupFilter() { 115 | fab.setOnClickListener(new View.OnClickListener() { 116 | @Override 117 | public void onClick(View v) { 118 | show(); 119 | } 120 | }); 121 | 122 | resultsScrim.setOnClickListener(new View.OnClickListener() { 123 | @Override 124 | public void onClick(View v) { 125 | hide(); 126 | } 127 | }); 128 | 129 | saveConfirmed.setOnClickListener(new View.OnClickListener() { 130 | @Override 131 | public void onClick(View v) { 132 | saveAndhide(); 133 | } 134 | }); 135 | 136 | tvLanguage.setText(Utils.getLanguagePreference(getActivity())); 137 | tvTags.setText(Utils.getTagsPreferenceString(getActivity())); 138 | 139 | scrollView.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() { 140 | @Override 141 | public void onScrollChange(NestedScrollView view, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { 142 | if (scrollY > oldScrollY) { 143 | if (fab.getVisibility() == View.VISIBLE) { 144 | FabAnimationUtils.scaleOut(fab); 145 | } 146 | } 147 | if (scrollY < oldScrollY) { 148 | if (fab.getVisibility() != View.VISIBLE) { 149 | fab.setVisibility(View.VISIBLE); 150 | FabAnimationUtils.scaleIn(fab); 151 | } 152 | } 153 | if(view.getChildAt(view.getChildCount() - 1) != null) { 154 | if ((scrollY >= (view.getChildAt(view.getChildCount() - 1).getMeasuredHeight() - view.getMeasuredHeight())) && 155 | scrollY > oldScrollY) { 156 | if (!loading) { 157 | page++; 158 | fetchIssues(); 159 | } 160 | } 161 | } 162 | 163 | } 164 | }); 165 | 166 | } 167 | 168 | @OnClick(R.id.tv_language) 169 | public void showLanguageDialog() { 170 | final String[] array = Utils.getLanguagesArray(); 171 | new MaterialDialog.Builder(getActivity()) 172 | .title("Select language") 173 | .items(Utils.getLanguagesArray()) 174 | .itemsCallbackSingleChoice(Arrays.asList(array).indexOf(Utils.getLanguagePreference(getActivity())), new MaterialDialog.ListCallbackSingleChoice() { 175 | @Override 176 | public boolean onSelection(MaterialDialog dialog, View view, int which, CharSequence text) { 177 | tvLanguage.setText(array[which]); 178 | Utils.setLanguagePreference(getActivity(), array[which]); 179 | return true; 180 | } 181 | }) 182 | .show(); 183 | } 184 | 185 | @OnClick(R.id.tv_tags) 186 | public void showTagsDialog() { 187 | final List tagsList = Arrays.asList(Utils.getTagsArray()); 188 | String[] tagsPreference = Utils.getTagsPreference(getActivity()); 189 | Integer[] tagsPreferenceIndex = {}; 190 | 191 | if (tagsPreference != null) { 192 | tagsPreferenceIndex = new Integer[tagsPreference.length]; 193 | 194 | int index = 0; 195 | for (String tagPreference: tagsPreference) { 196 | tagsPreferenceIndex[index++] = tagsList.indexOf(tagPreference); 197 | } 198 | } 199 | 200 | new MaterialDialog.Builder(getActivity()) 201 | .title("Select Tags") 202 | .items(Utils.getTagsArray()) 203 | .itemsCallbackMultiChoice(tagsPreferenceIndex, new MaterialDialog.ListCallbackMultiChoice() { 204 | @Override 205 | public boolean onSelection(MaterialDialog dialog, Integer[] which, CharSequence[] text) { 206 | String[] selectedTags = new String[text.length]; 207 | int index = 0; 208 | 209 | for (CharSequence charSequence: text) { 210 | selectedTags[index++] = charSequence.toString(); 211 | } 212 | 213 | Utils.setTagsPreference(getActivity(), selectedTags); 214 | tvTags.setText(Utils.getTagsPreferenceString(getActivity())); 215 | return true; 216 | }; 217 | } 218 | ).positiveText("Select Tags") 219 | .show(); 220 | } 221 | 222 | private void setupRecyclerview() { 223 | 224 | final LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity()); 225 | recyclerView.setLayoutManager(layoutManager); 226 | 227 | adapter = new ProjectsAdapter(getActivity()); 228 | recyclerView.setAdapter(adapter); 229 | 230 | recyclerView.setNestedScrollingEnabled(false); 231 | 232 | } 233 | 234 | private void fetchIssues() { 235 | loading = true; 236 | swipeRefreshLayout.setEnabled(false); 237 | if(page == 1) { 238 | progressBar.setVisibility(View.VISIBLE); 239 | adapter.clearData(); 240 | } 241 | else 242 | swipeRefreshLayout.setRefreshing(true); 243 | String language = Utils.getLanguagePreference(getActivity()); 244 | 245 | String[] tags = Utils.getTagsPreference(getActivity()); 246 | GithubRepository.getInstance().findIssues(language, tags, page) 247 | .observeOn(AndroidSchedulers.mainThread()) 248 | .subscribeOn(Schedulers.io()) 249 | .subscribe(new Subscriber>() { 250 | @Override 251 | public void onCompleted() { 252 | 253 | } 254 | 255 | @Override 256 | public void onError(Throwable e) { 257 | progressBar.setVisibility(View.GONE); 258 | swipeRefreshLayout.setRefreshing(false); 259 | swipeRefreshLayout.setEnabled(true); 260 | loading = false; 261 | if (getActivity() != null) { 262 | Toast.makeText(getActivity(), "Error fetching projects", Toast.LENGTH_SHORT).show(); 263 | } 264 | } 265 | 266 | @Override 267 | public void onNext(List response) { 268 | progressBar.setVisibility(View.GONE); 269 | swipeRefreshLayout.setRefreshing(false); 270 | swipeRefreshLayout.setEnabled(true); 271 | loading = false; 272 | if(page == 1) 273 | adapter.setData(response); 274 | else 275 | adapter.addData(response); 276 | } 277 | }); 278 | } 279 | 280 | private void show() { 281 | FabAnimationUtils.scaleOut(fab); 282 | fab.setVisibility(View.INVISIBLE); 283 | confirmSaveContainer.setVisibility(View.VISIBLE); 284 | resultsScrim.setVisibility(View.VISIBLE); 285 | 286 | confirmSaveContainer.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver 287 | .OnPreDrawListener() { 288 | @Override 289 | public boolean onPreDraw() { 290 | confirmSaveContainer.getViewTreeObserver().removeOnPreDrawListener(this); 291 | 292 | Animator reveal = ViewAnimationUtils.createCircularReveal(confirmSaveContainer, 293 | confirmSaveContainer.getWidth() / 2, 294 | confirmSaveContainer.getHeight() / 2, 295 | fab.getWidth() / 2, 296 | confirmSaveContainer.getWidth() / 2); 297 | reveal.setDuration(250L); 298 | reveal.setInterpolator(AnimUtils.getFastOutSlowInInterpolator(getActivity())); 299 | reveal.start(); 300 | 301 | int centerX = (fab.getLeft() + fab.getRight()) / 2; 302 | int centerY = (fab.getTop() + fab.getBottom()) / 2; 303 | Animator revealScrim = ViewAnimationUtils.createCircularReveal( 304 | resultsScrim, 305 | centerX, 306 | centerY, 307 | 0, 308 | (float) Math.hypot(centerX, centerY)); 309 | revealScrim.setDuration(400L); 310 | revealScrim.setInterpolator(AnimUtils.getLinearOutSlowInInterpolator(getActivity() 311 | )); 312 | revealScrim.start(); 313 | ObjectAnimator fadeInScrim = ObjectAnimator.ofArgb(resultsScrim, 314 | Utils.BACKGROUND_COLOR, 315 | Color.TRANSPARENT, 316 | ContextCompat.getColor(getActivity(), R.color.scrim)); 317 | fadeInScrim.setDuration(800L); 318 | fadeInScrim.setInterpolator(AnimUtils.getLinearOutSlowInInterpolator(getActivity() 319 | )); 320 | fadeInScrim.start(); 321 | 322 | return false; 323 | } 324 | }); 325 | } 326 | 327 | private void hide() { 328 | if (confirmSaveContainer.getVisibility() == View.VISIBLE) { 329 | hideFilterContainer(); 330 | } 331 | } 332 | 333 | private void saveAndhide() { 334 | if (confirmSaveContainer.getVisibility() == View.VISIBLE) { 335 | page=1; 336 | fetchIssues(); 337 | hideFilterContainer(); 338 | } 339 | } 340 | 341 | private void hideFilterContainer() { 342 | 343 | AnimatorSet hideConfirmation = new AnimatorSet(); 344 | hideConfirmation.playTogether( 345 | ViewAnimationUtils.createCircularReveal(confirmSaveContainer, 346 | confirmSaveContainer.getWidth() / 2, 347 | confirmSaveContainer.getHeight() / 2, 348 | confirmSaveContainer.getWidth() / 2, 349 | fab.getWidth() / 2), 350 | ObjectAnimator.ofArgb(resultsScrim, 351 | Utils.BACKGROUND_COLOR, 352 | Color.TRANSPARENT)); 353 | hideConfirmation.setDuration(150L); 354 | hideConfirmation.setInterpolator(AnimUtils.getFastOutSlowInInterpolator 355 | (getActivity())); 356 | hideConfirmation.addListener(new AnimatorListenerAdapter() { 357 | @Override 358 | public void onAnimationEnd(Animator animation) { 359 | confirmSaveContainer.setVisibility(View.GONE); 360 | resultsScrim.setVisibility(View.GONE); 361 | fab.setVisibility(View.VISIBLE); 362 | FabAnimationUtils.scaleIn(fab); 363 | } 364 | }); 365 | hideConfirmation.start(); 366 | 367 | } 368 | 369 | } 370 | -------------------------------------------------------------------------------- /app/src/main/java/com/naman14/hacktoberfest/fragment/StatusFragment.java: -------------------------------------------------------------------------------- 1 | package com.naman14.hacktoberfest.fragment; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.graphics.Point; 6 | import android.os.Bundle; 7 | import android.os.Handler; 8 | import androidx.annotation.Nullable; 9 | import com.google.android.material.snackbar.Snackbar; 10 | import androidx.fragment.app.Fragment; 11 | import androidx.core.widget.NestedScrollView; 12 | import androidx.recyclerview.widget.GridLayoutManager; 13 | import androidx.recyclerview.widget.RecyclerView; 14 | import android.view.KeyEvent; 15 | import android.view.LayoutInflater; 16 | import android.view.View; 17 | import android.view.ViewGroup; 18 | import android.view.ViewParent; 19 | import android.view.inputmethod.EditorInfo; 20 | import android.widget.EditText; 21 | import android.widget.ImageView; 22 | import android.widget.ProgressBar; 23 | import android.widget.TextView; 24 | import android.widget.Toast; 25 | 26 | import com.naman14.hacktoberfest.R; 27 | import com.naman14.hacktoberfest.adapters.PRAdapter; 28 | import com.naman14.hacktoberfest.network.entity.Issue; 29 | import com.naman14.hacktoberfest.network.repository.GithubRepository; 30 | import com.naman14.hacktoberfest.utils.Utils; 31 | import com.naman14.hacktoberfest.widgets.GridRecyclerView; 32 | import com.squareup.picasso.Picasso; 33 | 34 | import java.util.List; 35 | 36 | import butterknife.BindView; 37 | import butterknife.ButterKnife; 38 | import butterknife.OnClick; 39 | import rx.Subscriber; 40 | import rx.android.schedulers.AndroidSchedulers; 41 | import rx.schedulers.Schedulers; 42 | 43 | /** 44 | * Created by naman on 4/10/17. 45 | */ 46 | 47 | public class StatusFragment extends Fragment { 48 | 49 | @BindView(R.id.progressBar) 50 | ProgressBar progressBar; 51 | 52 | @BindView(R.id.iv_user_image) 53 | ImageView ivUserImage; 54 | 55 | @BindView(R.id.et_username) 56 | EditText etUsername; 57 | 58 | @BindView(R.id.tv_placeholder) 59 | TextView tvPlaceholder; 60 | 61 | @BindView(R.id.tv_pr_count) 62 | TextView tvPrCount; 63 | 64 | @BindView(R.id.tv_status_message) 65 | TextView tvStatusMessage; 66 | 67 | @BindView(R.id.tv_status_extra_message) 68 | TextView tvStatusExtraMessage; 69 | 70 | @BindView(R.id.iv_check) 71 | ImageView ivCheck; 72 | 73 | @BindView(R.id.scrollView) 74 | NestedScrollView scrollView; 75 | 76 | @BindView(R.id.statusView) 77 | View statusView; 78 | 79 | @BindView(R.id.recyclerView) 80 | GridRecyclerView recyclerView; 81 | 82 | private PRAdapter adapter; 83 | private SharedPreferences prefs; 84 | 85 | private static final String SHARED_PREFS = "hacktoberfest-android"; 86 | private static final String USERNAME_KEY = "username"; 87 | 88 | @Nullable 89 | @Override 90 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 91 | View rootView = inflater.inflate(R.layout.fragment_status, container, false); 92 | 93 | ButterKnife.bind(this, rootView); 94 | 95 | setupRecyclerview(); 96 | 97 | prefs = getActivity() 98 | .getSharedPreferences(SHARED_PREFS, Context.MODE_PRIVATE); 99 | 100 | String storedUsername = prefs.getString(USERNAME_KEY, ""); 101 | if(!storedUsername.isEmpty()) { 102 | etUsername.setText(storedUsername); 103 | checkPRStatus(); 104 | } 105 | 106 | etUsername.setOnEditorActionListener(new TextView.OnEditorActionListener() { 107 | @Override 108 | public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { 109 | if (actionId == EditorInfo.IME_ACTION_DONE) { 110 | Utils.hideKeyboard(getActivity()); 111 | checkPRStatus(); 112 | return true; 113 | } 114 | return false; 115 | } 116 | }); 117 | 118 | 119 | return rootView; 120 | } 121 | 122 | 123 | @OnClick(R.id.iv_check) 124 | public void checkClicked() { 125 | String username = etUsername.getText().toString(); 126 | 127 | if(username.isEmpty() || username.trim().length() == 0){ 128 | Toast.makeText(getContext(), "Please enter a valid github username", Toast.LENGTH_LONG).show(); 129 | }else{ 130 | checkPRStatus(); 131 | } 132 | } 133 | 134 | private void setupRecyclerview() { 135 | RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getActivity(), 2); 136 | recyclerView.setLayoutManager(layoutManager); 137 | 138 | adapter = new PRAdapter(getActivity()); 139 | recyclerView.setAdapter(adapter); 140 | 141 | recyclerView.setNestedScrollingEnabled(false); 142 | } 143 | 144 | private void checkPRStatus() { 145 | tvPlaceholder.setVisibility(View.GONE); 146 | statusView.setVisibility(View.GONE); 147 | progressBar.setVisibility(View.VISIBLE); 148 | 149 | prefs 150 | .edit() 151 | .putString(USERNAME_KEY, etUsername.getText().toString()) 152 | .apply(); 153 | 154 | GithubRepository.getInstance().findValidPRs(etUsername.getText().toString()) 155 | .observeOn(AndroidSchedulers.mainThread()) 156 | .subscribeOn(Schedulers.io()) 157 | .subscribe(new Subscriber>() { 158 | @Override 159 | public void onCompleted() { 160 | 161 | } 162 | 163 | @Override 164 | public void onError(Throwable e) { 165 | progressBar.setVisibility(View.GONE); 166 | Snackbar.make(scrollView, "Error fetching details", Snackbar.LENGTH_LONG).show(); 167 | } 168 | 169 | @Override 170 | public void onNext(List response) { 171 | progressBar.setVisibility(View.GONE); 172 | showStatus(response); 173 | 174 | } 175 | }); 176 | } 177 | 178 | private void showStatus(final List response) { 179 | progressBar.setVisibility(View.GONE); 180 | statusView.setVisibility(View.VISIBLE); 181 | 182 | if (response != null && response.size() != 0) { 183 | Picasso.with(getActivity()).load(response.get(0).getUser().getAvatar_url()).into(ivUserImage); 184 | } else { 185 | Picasso.with(getActivity()).load("https://github.com/" + 186 | etUsername.getText().toString() + ".png").into(ivUserImage); 187 | } 188 | 189 | if (response != null) { 190 | tvPrCount.setText(String.valueOf(response.size())); 191 | tvStatusMessage.setText(Utils.getStatusMessage(response.size())); 192 | if (response.size() > 0) 193 | tvStatusExtraMessage.setVisibility(View.VISIBLE); 194 | adapter.setData(response); 195 | } else { 196 | tvPrCount.setText("0"); 197 | } 198 | 199 | Handler handler = new Handler(); 200 | handler.postDelayed(new Runnable() { 201 | @Override 202 | public void run() { 203 | scrollToView(scrollView, etUsername); 204 | 205 | } 206 | }, 350); 207 | 208 | Handler handler2 = new Handler(); 209 | handler2.postDelayed(new Runnable() { 210 | @Override 211 | public void run() { 212 | adapter.setData(response); 213 | 214 | } 215 | }, 450); 216 | 217 | } 218 | 219 | 220 | private void scrollToView(final NestedScrollView scrollViewParent, final View view) { 221 | // Get deepChild Offset 222 | Point childOffset = new Point(); 223 | getDeepChildOffset(scrollViewParent, view.getParent(), view, childOffset); 224 | // Scroll to child. 225 | scrollViewParent.smoothScrollTo(0, childOffset.y - 50); 226 | } 227 | 228 | private void getDeepChildOffset(final ViewGroup mainParent, final ViewParent parent, final View child, final Point accumulatedOffset) { 229 | ViewGroup parentGroup = (ViewGroup) parent; 230 | accumulatedOffset.x += child.getLeft(); 231 | accumulatedOffset.y += child.getTop(); 232 | if (parentGroup.equals(mainParent)) { 233 | return; 234 | } 235 | getDeepChildOffset(mainParent, parentGroup.getParent(), parentGroup, accumulatedOffset); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /app/src/main/java/com/naman14/hacktoberfest/network/api/ApiEndPoints.java: -------------------------------------------------------------------------------- 1 | package com.naman14.hacktoberfest.network.api; 2 | 3 | /** 4 | * Created by naman on 17/6/17. 5 | */ 6 | 7 | public class ApiEndPoints { 8 | 9 | public static final String SEARCH = "search"; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/naman14/hacktoberfest/network/api/ApiInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.naman14.hacktoberfest.network.api; 2 | 3 | import android.text.TextUtils; 4 | 5 | import java.io.IOException; 6 | 7 | import okhttp3.Interceptor; 8 | import okhttp3.Request; 9 | import okhttp3.Request.Builder; 10 | import okhttp3.Response; 11 | 12 | /** 13 | * Created by naman on 17/6/17. 14 | */ 15 | 16 | public class ApiInterceptor implements Interceptor { 17 | 18 | private static final String HEADER_AUTH = "Authorization"; 19 | private static final String HEADER_ACCEPT = "Accept"; 20 | private static final String HEADER_USER_AGENT = "User-Agent"; 21 | private String authToken; 22 | 23 | 24 | public ApiInterceptor(String authToken) { 25 | this.authToken = authToken; 26 | 27 | } 28 | 29 | @Override 30 | public Response intercept(Chain chain) throws IOException { 31 | Request chainRequest = chain.request(); 32 | Builder builder = chainRequest.newBuilder(); 33 | builder.header(HEADER_ACCEPT, "application/vnd.github.v3+json"); 34 | builder.header(HEADER_USER_AGENT, "Hacktoberfest-Android-App"); 35 | if (!TextUtils.isEmpty(authToken)) { 36 | builder.header(HEADER_AUTH, "Bearer " + authToken); 37 | } 38 | 39 | Request request = builder.build(); 40 | return chain.proceed(request); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/naman14/hacktoberfest/network/api/BaseURL.java: -------------------------------------------------------------------------------- 1 | package com.naman14.hacktoberfest.network.api; 2 | 3 | /** 4 | * Created by naman on 17/6/17. 5 | */ 6 | 7 | public class BaseURL { 8 | 9 | public static final String PROTOCOL_HTTPS = "https://"; 10 | 11 | public static final String API_ENDPOINT = "api.github.com"; 12 | 13 | 14 | public String getUrl() { 15 | return PROTOCOL_HTTPS + API_ENDPOINT; 16 | 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/naman14/hacktoberfest/network/api/GithubApiManager.java: -------------------------------------------------------------------------------- 1 | package com.naman14.hacktoberfest.network.api; 2 | 3 | 4 | import com.naman14.hacktoberfest.network.api.services.SearchService; 5 | 6 | import okhttp3.OkHttpClient; 7 | import okhttp3.logging.HttpLoggingInterceptor; 8 | import retrofit2.Retrofit; 9 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 10 | import retrofit2.converter.gson.GsonConverterFactory; 11 | 12 | /** 13 | * Created by naman on 17/6/17. 14 | */ 15 | 16 | public class GithubApiManager { 17 | 18 | private static BaseURL baseUrl = new BaseURL(); 19 | private static final String BASE_URL = baseUrl.getUrl(); 20 | 21 | private static Retrofit retrofit; 22 | 23 | private static SearchService searchApi; 24 | 25 | 26 | public GithubApiManager() { 27 | String authToken = ""; 28 | createService(authToken); 29 | } 30 | 31 | private static void init() { 32 | searchApi = createApi(SearchService.class); 33 | } 34 | 35 | private static T createApi(Class clazz) { 36 | return retrofit.create(clazz); 37 | } 38 | 39 | public static void createService(String authToken) { 40 | 41 | HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); 42 | interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 43 | 44 | OkHttpClient okHttpClient = new OkHttpClient.Builder() 45 | .addInterceptor(interceptor) 46 | .addInterceptor(new ApiInterceptor(authToken)) 47 | .build(); 48 | 49 | retrofit = new Retrofit.Builder() 50 | .baseUrl(BASE_URL) 51 | .addConverterFactory(GsonConverterFactory.create()) 52 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 53 | .client(okHttpClient) 54 | .build(); 55 | init(); 56 | 57 | } 58 | 59 | public SearchService getSearchApi() { 60 | return searchApi; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/naman14/hacktoberfest/network/api/services/SearchService.java: -------------------------------------------------------------------------------- 1 | package com.naman14.hacktoberfest.network.api.services; 2 | 3 | import com.naman14.hacktoberfest.network.api.ApiEndPoints; 4 | import com.naman14.hacktoberfest.network.entity.SearchResponse; 5 | 6 | import retrofit2.http.GET; 7 | import retrofit2.http.Query; 8 | import rx.Observable; 9 | 10 | /** 11 | * Created by naman on 4/10/17. 12 | */ 13 | 14 | public interface SearchService { 15 | 16 | @GET(ApiEndPoints.SEARCH + "/issues") 17 | Observable findPRs(@Query(encoded = true, value = "q") String searchQuery); 18 | 19 | @GET(ApiEndPoints.SEARCH + "/issues") 20 | Observable findIssues(@Query(encoded = true, value = "q") String searchQuery, @Query(encoded = true, value = "page") int page); 21 | 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/naman14/hacktoberfest/network/entity/Issue.java: -------------------------------------------------------------------------------- 1 | package com.naman14.hacktoberfest.network.entity; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by naman on 5/10/17. 7 | */ 8 | 9 | public class Issue { 10 | 11 | private String html_url, title, body, createdAt, repository_url, state; 12 | private int id, number; 13 | 14 | private String language; 15 | 16 | private User user; 17 | 18 | private List