X Tutup
Skip to content

Commit b9cbd5d

Browse files
author
David Weiser
committed
adds github issue tracker exercise
1 parent cf9d1bc commit b9cbd5d

File tree

33 files changed

+802
-0
lines changed

33 files changed

+802
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
apply plugin: 'com.android.application'
2+
3+
android {
4+
compileSdkVersion 25
5+
buildToolsVersion "25.0.2"
6+
defaultConfig {
7+
applicationId "com.vogella.android.github.issuetracker"
8+
minSdkVersion 15
9+
targetSdkVersion 25
10+
versionCode 1
11+
versionName "1.0"
12+
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
13+
}
14+
buildTypes {
15+
release {
16+
minifyEnabled false
17+
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18+
}
19+
}
20+
}
21+
22+
dependencies {
23+
compile fileTree(dir: 'libs', include: ['*.jar'])
24+
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
25+
exclude group: 'com.android.support', module: 'support-annotations'
26+
})
27+
compile 'com.android.support:appcompat-v7:25.3.1'
28+
compile 'com.squareup.retrofit2:retrofit:2.2.0'
29+
compile 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
30+
compile 'com.squareup.retrofit2:converter-gson:2.2.0'
31+
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
32+
compile 'com.squareup.okhttp3:logging-interceptor:3.7.0'
33+
testCompile 'junit:junit:4.12'
34+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Add project specific ProGuard rules here.
2+
# By default, the flags in this file are appended to flags specified
3+
# in /home/david/Android/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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.vogella.android.github.issuetracker;
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 <a href="http://d.android.com/tools/testing">Testing documentation</a>
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.vogella.android.github.issuetracker", appContext.getPackageName());
25+
}
26+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="com.vogella.android.github.issuetracker">
4+
5+
<uses-permission android:name="android.permission.INTERNET" />
6+
<application
7+
android:allowBackup="true"
8+
android:icon="@mipmap/ic_launcher"
9+
android:label="@string/app_name"
10+
android:roundIcon="@mipmap/ic_launcher_round"
11+
android:supportsRtl="true"
12+
android:theme="@style/AppTheme">
13+
<activity android:name=".MainActivity">
14+
<intent-filter>
15+
<action android:name="android.intent.action.MAIN" />
16+
17+
<category android:name="android.intent.category.LAUNCHER" />
18+
</intent-filter>
19+
</activity>
20+
</application>
21+
22+
</manifest>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.vogella.android.github.issuetracker;
2+
3+
import java.util.List;
4+
5+
import io.reactivex.android.schedulers.AndroidSchedulers;
6+
import io.reactivex.observers.DisposableSingleObserver;
7+
import io.reactivex.schedulers.Schedulers;
8+
import okhttp3.Credentials;
9+
import okhttp3.OkHttpClient;
10+
import okhttp3.logging.HttpLoggingInterceptor;
11+
import retrofit2.Retrofit;
12+
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
13+
import retrofit2.converter.gson.GsonConverterFactory;
14+
15+
public class CommunicationController {
16+
17+
private GithubApi githubApi;
18+
19+
public CommunicationController() {
20+
initGithubApi();
21+
}
22+
23+
private void initGithubApi() {
24+
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
25+
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
26+
OkHttpClient okHttpClient = new OkHttpClient.Builder().addInterceptor(interceptor).build();
27+
Retrofit retrofit = new Retrofit.Builder()
28+
.client(okHttpClient)
29+
.baseUrl(GithubApi.BASE_URL)
30+
.addConverterFactory(GsonConverterFactory.create())
31+
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
32+
.build();
33+
34+
githubApi = retrofit.create(GithubApi.class);
35+
}
36+
37+
public void loadIssues(String username, String password, DisposableSingleObserver<List<Issue>> observer, String owner, String repository) {
38+
githubApi.getIssues(Credentials.basic(username, password), owner, repository)
39+
.subscribeOn(Schedulers.io())
40+
.observeOn(AndroidSchedulers.mainThread())
41+
.subscribe(observer);
42+
}
43+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.vogella.android.github.issuetracker;
2+
3+
import android.app.Dialog;
4+
import android.content.Context;
5+
import android.content.DialogInterface;
6+
import android.os.Bundle;
7+
import android.support.annotation.NonNull;
8+
import android.support.v4.app.DialogFragment;
9+
import android.support.v7.app.AlertDialog;
10+
import android.view.View;
11+
import android.widget.EditText;
12+
13+
public class CredentialsDialog extends DialogFragment {
14+
15+
EditText usernameEditText;
16+
EditText passwordEditText;
17+
ICredentialsDialogListener listener;
18+
19+
public interface ICredentialsDialogListener {
20+
void onDialogPositiveClick(String username, String password);
21+
}
22+
23+
@Override
24+
public void onAttach(Context context) {
25+
super.onAttach(context);
26+
if (getActivity() instanceof ICredentialsDialogListener) {
27+
listener = (ICredentialsDialogListener) getActivity();
28+
}
29+
}
30+
31+
@NonNull
32+
@Override
33+
public Dialog onCreateDialog(Bundle savedInstanceState) {
34+
View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_credentials, null);
35+
usernameEditText = (EditText) view.findViewById(R.id.username_edittext);
36+
passwordEditText = (EditText) view.findViewById(R.id.password_edittext);
37+
38+
usernameEditText.setText(getArguments().getString("username"));
39+
passwordEditText.setText(getArguments().getString("password"));
40+
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
41+
.setView(view)
42+
.setTitle("Credentials")
43+
.setNegativeButton("Cancel", null)
44+
.setPositiveButton("Store", new DialogInterface.OnClickListener() {
45+
@Override
46+
public void onClick(DialogInterface dialog, int which) {
47+
if (listener != null) {
48+
listener.onDialogPositiveClick(usernameEditText.getText().toString(), passwordEditText.getText().toString());
49+
}
50+
}
51+
});
52+
return builder.create();
53+
}
54+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.vogella.android.github.issuetracker;
2+
3+
import java.util.List;
4+
5+
import io.reactivex.Single;
6+
import retrofit2.http.GET;
7+
import retrofit2.http.Header;
8+
import retrofit2.http.Path;
9+
10+
public interface GithubApi {
11+
12+
String BASE_URL = "https://api.github.com";
13+
14+
@GET("/repos/{owner}/{repository}/issues")
15+
Single<List<Issue>> getIssues(@Header("Authorization") String credentials, @Path("owner") String owner, @Path("repository") String repository);
16+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.vogella.android.github.issuetracker;
2+
3+
class Issue {
4+
public int id;
5+
public String title;
6+
7+
@Override
8+
public String toString() {
9+
return id + " - " + title;
10+
}
11+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package com.vogella.android.github.issuetracker;
2+
3+
import android.support.v7.app.AppCompatActivity;
4+
import android.os.Bundle;
5+
import android.support.v7.widget.Toolbar;
6+
import android.view.KeyEvent;
7+
import android.view.Menu;
8+
import android.view.MenuItem;
9+
import android.view.View;
10+
import android.view.inputmethod.EditorInfo;
11+
import android.widget.EditText;
12+
import android.widget.TextView;
13+
import android.widget.Toast;
14+
15+
import java.util.List;
16+
17+
import io.reactivex.observers.DisposableSingleObserver;
18+
19+
public class MainActivity extends AppCompatActivity implements CredentialsDialog.ICredentialsDialogListener {
20+
21+
EditText ownerEditText;
22+
EditText repositoryEditText;
23+
TextView issuesTextView;
24+
private String password = "";
25+
private String username= "";
26+
private CommunicationController communicationController;
27+
28+
@Override
29+
protected void onCreate(Bundle savedInstanceState) {
30+
super.onCreate(savedInstanceState);
31+
setContentView(R.layout.activity_main);
32+
Toolbar toolbar = (Toolbar) findViewById(R.id.my_toolbar);
33+
setSupportActionBar(toolbar);
34+
35+
ownerEditText = (EditText) findViewById(R.id.owner_edittext);
36+
repositoryEditText = (EditText) findViewById(R.id.repository_edittext);
37+
repositoryEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
38+
@Override
39+
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
40+
if(actionId == EditorInfo.IME_ACTION_DONE){
41+
queryForIssues();
42+
return true;
43+
}
44+
return false;
45+
}
46+
});
47+
issuesTextView = (TextView) findViewById(R.id.issues_text);
48+
49+
communicationController = new CommunicationController();
50+
}
51+
52+
@Override
53+
public boolean onCreateOptionsMenu(Menu menu) {
54+
getMenuInflater().inflate(R.menu.menu_main, menu);
55+
return true;
56+
}
57+
58+
@Override
59+
public boolean onOptionsItemSelected(MenuItem item) {
60+
switch (item.getItemId()) {
61+
case R.id.menu_credentials:
62+
showCredentialsDialog();
63+
return true;
64+
case R.id.menu_refresh:
65+
queryForIssues();
66+
return true;
67+
}
68+
return super.onOptionsItemSelected(item);
69+
}
70+
71+
private DisposableSingleObserver<List<Issue>> getIssuesObserver() {
72+
return new DisposableSingleObserver<List<Issue>>() {
73+
@Override
74+
public void onSuccess(List<Issue> value) {
75+
for (Issue issues : value) {
76+
issuesTextView.append(issues.toString() + "\n");
77+
}
78+
}
79+
80+
@Override
81+
public void onError(Throwable e) {
82+
Toast.makeText(MainActivity.this, "Cannot load issues.", Toast.LENGTH_SHORT).show();
83+
}
84+
};
85+
}
86+
87+
private void showCredentialsDialog() {
88+
CredentialsDialog dialog = new CredentialsDialog();
89+
Bundle arguments = new Bundle();
90+
arguments.putString("username", username);
91+
arguments.putString("password", password);
92+
dialog.setArguments(arguments);
93+
94+
dialog.show(getSupportFragmentManager(), "credentialsDialog");
95+
}
96+
97+
@Override
98+
public void onDialogPositiveClick(String username, String password) {
99+
this.username = username;
100+
this.password = password;
101+
}
102+
103+
private void queryForIssues() {
104+
issuesTextView.setText("");
105+
String owner = ownerEditText.getText().toString();
106+
String repository = repositoryEditText.getText().toString();
107+
if(!username.isEmpty() && !password.isEmpty() && !owner.isEmpty() && !repository.isEmpty()) {
108+
communicationController.loadIssues(username, password, getIssuesObserver(), owner, repository);
109+
}
110+
}
111+
}

0 commit comments

Comments
 (0)
X Tutup