Set up app-mvvm module

This commit is contained in:
Ivan Carballo
2015-09-15 15:55:54 +01:00
parent 08967fd55c
commit b301821400
28 changed files with 1084 additions and 3 deletions

View File

@@ -1,4 +1,4 @@
package uk.ivanc.archimvp.view;
package uk.ivanc.archimvp;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
@@ -10,7 +10,6 @@ import android.widget.TextView;
import java.util.Collections;
import java.util.List;
import uk.ivanc.archimvp.R;
import uk.ivanc.archimvp.model.Repository;
public class RepositoryAdapter extends RecyclerView.Adapter<RepositoryAdapter.RepositoryViewHolder> {

View File

@@ -17,6 +17,7 @@ import android.widget.TextView;
import java.util.List;
import uk.ivanc.archimvp.R;
import uk.ivanc.archimvp.RepositoryAdapter;
import uk.ivanc.archimvp.model.Repository;
import uk.ivanc.archimvp.presenter.MainPresenter;

1
app-mvvm/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

34
app-mvvm/build.gradle Normal file
View File

@@ -0,0 +1,34 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion rootProject.ext.androidCompileSdkVersion
buildToolsVersion rootProject.ext.androidBuildToolsVersion
defaultConfig {
applicationId "uk.ivanc.archimvvm"
minSdkVersion rootProject.ext.androidMinSdkVersion
targetSdkVersion rootProject.ext.androidTargetSdkVersion
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
Map<String, String> dependencies = rootProject.ext.dependencies;
compile dependencies.appCompat
compile dependencies.cardView
compile dependencies.recyclerView
compile dependencies.retrofit
compile dependencies.retrofitConverterGson
compile dependencies.retrofitAdapterRxJava
compile dependencies.picasso
compile dependencies.rxAndroid
compile dependencies.circleImageView
}

17
app-mvvm/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/ivan/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@@ -0,0 +1,13 @@
package uk.ivanc.archimvvm;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="uk.ivanc.archimvvm">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:name=".ArchiApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".RepositoryActivity"
android:theme="@style/AppTheme.NoActionBar"
android:parentActivityName=".MainActivity"/>
</application>
</manifest>

View File

@@ -0,0 +1,22 @@
package uk.ivanc.archimvvm;
import android.app.Application;
import android.content.Context;
import uk.ivanc.archimvvm.model.GithubService;
public class ArchiApplication extends Application {
private GithubService githubService;
public static ArchiApplication get(Context context) {
return (ArchiApplication) context.getApplicationContext();
}
public GithubService getGithubService() {
if (githubService == null) {
githubService = GithubService.Factory.create();
}
return githubService;
}
}

View File

@@ -0,0 +1,135 @@
package uk.ivanc.archimvvm;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.List;
import retrofit.HttpException;
import rx.Subscriber;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
import uk.ivanc.archimvvm.model.GithubService;
import uk.ivanc.archimvvm.model.Repository;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Subscription subscription;
private RecyclerView reposRecycleView;
private Toolbar toolbar;
private EditText editTextUsername;
private ProgressBar progressBar;
private TextView infoTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = (ProgressBar) findViewById(R.id.progress);
infoTextView = (TextView) findViewById(R.id.text_info);
//Set up ToolBar
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//Set up RecyclerView
reposRecycleView = (RecyclerView) findViewById(R.id.repos_recycler_view);
setupRecyclerView(reposRecycleView);
//Set up username EditText
editTextUsername = (EditText) findViewById(R.id.edit_text_username);
editTextUsername.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
String username = editTextUsername.getText().toString();
if (username.length() > 0) loadGithubRepos(username);
return true;
}
return false;
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
if (subscription != null) subscription.unsubscribe();
}
public void loadGithubRepos(String username) {
progressBar.setVisibility(View.VISIBLE);
reposRecycleView.setVisibility(View.GONE);
infoTextView.setVisibility(View.GONE);
GithubService githubService = ArchiApplication.get(this).getGithubService();
subscription = githubService.publicRepositories(username)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<List<Repository>>() {
@Override
public void onCompleted() {
progressBar.setVisibility(View.GONE);
if (reposRecycleView.getAdapter().getItemCount() > 0) {
reposRecycleView.requestFocus();
hideSoftKeyboard();
reposRecycleView.setVisibility(View.VISIBLE);
} else {
infoTextView.setText(R.string.text_empty_repos);
infoTextView.setVisibility(View.VISIBLE);
}
}
@Override
public void onError(Throwable error) {
Log.e(TAG, "Error loading GitHub repos ", error);
progressBar.setVisibility(View.GONE);
if (error instanceof HttpException
&& ((HttpException) error).code() == 404) {
infoTextView.setText(R.string.error_username_not_found);
} else {
infoTextView.setText(R.string.error_loading_repos);
}
infoTextView.setVisibility(View.VISIBLE);
}
@Override
public void onNext(List<Repository> repositories) {
Log.i(TAG, "Repos loaded " + repositories);
RepositoryAdapter adapter =
(RepositoryAdapter) reposRecycleView.getAdapter();
adapter.setRepositories(repositories);
adapter.notifyDataSetChanged();
}
});
}
private void setupRecyclerView(RecyclerView recyclerView) {
RepositoryAdapter adapter = new RepositoryAdapter();
adapter.setCallback(new RepositoryAdapter.Callback() {
@Override
public void onItemClick(Repository repository) {
startActivity(RepositoryActivity.newIntent(MainActivity.this, repository));
}
});
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}
private void hideSoftKeyboard() {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(editTextUsername.getWindowToken(), 0);
}
}

View File

@@ -0,0 +1,118 @@
package uk.ivanc.archimvvm;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.squareup.picasso.Picasso;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.schedulers.Schedulers;
import uk.ivanc.archimvvm.model.GithubService;
import uk.ivanc.archimvvm.model.Repository;
import uk.ivanc.archimvvm.model.User;
public class RepositoryActivity extends AppCompatActivity {
private static final String EXTRA_REPOSITORY = "EXTRA_REPOSITORY";
private static final String TAG = "RepositoryActivity";
private Toolbar toolbar;
private TextView descriptionText;
private TextView homepageText;
private TextView languageText;
private TextView forkText;
private TextView ownerNameText;
private TextView ownerEmailText;
private TextView ownerLocationText;
private ImageView ownerImage;
private View ownerLayout;
private Subscription subscription;
public static Intent newIntent(Context context, Repository repository) {
Intent intent = new Intent(context, RepositoryActivity.class);
intent.putExtra(EXTRA_REPOSITORY, repository);
return intent;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_repository);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
descriptionText = (TextView) findViewById(R.id.text_repo_description);
homepageText = (TextView) findViewById(R.id.text_homepage);
languageText = (TextView) findViewById(R.id.text_language);
forkText = (TextView) findViewById(R.id.text_fork);
ownerNameText = (TextView) findViewById(R.id.text_owner_name);
ownerEmailText = (TextView) findViewById(R.id.text_owner_email);
ownerLocationText = (TextView) findViewById(R.id.text_owner_location);
ownerImage = (ImageView) findViewById(R.id.image_owner);
ownerLayout = findViewById(R.id.layout_owner);
Repository repository = getIntent().getParcelableExtra(EXTRA_REPOSITORY);
bindRepositoryData(repository);
loadFullUser(repository.owner.url);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (subscription != null) subscription.unsubscribe();
}
private void bindRepositoryData(final Repository repository) {
setTitle(repository.name);
descriptionText.setText(repository.description);
homepageText.setText(repository.homepage);
homepageText.setVisibility(repository.hasHomepage() ? View.VISIBLE : View.GONE);
languageText.setText(getString(R.string.text_language, repository.language));
languageText.setVisibility(repository.hasLanguage() ? View.VISIBLE : View.GONE);
forkText.setVisibility(repository.isFork() ? View.VISIBLE : View.GONE);
//Preload image for user because we already have it before loading the full user
Picasso.with(this)
.load(repository.owner.avatarUrl)
.placeholder(R.drawable.placeholder)
.into(ownerImage);
}
private void bindOwnerData(final User owner) {
ownerNameText.setText(owner.name);
ownerEmailText.setText(owner.email);
ownerEmailText.setVisibility(owner.hasEmail() ? View.VISIBLE : View.GONE);
ownerLocationText.setText(owner.location);
ownerLocationText.setVisibility(owner.hasLocation() ? View.VISIBLE : View.GONE);
}
private void loadFullUser(String url) {
GithubService githubService = ArchiApplication.get(this).getGithubService();
subscription = githubService.userFromUrl(url)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Action1<User>() {
@Override
public void call(User user) {
Log.i(TAG, "Full user data loaded " + user);
bindOwnerData(user);
ownerLayout.setVisibility(View.VISIBLE);
}
});
}
}

View File

@@ -0,0 +1,95 @@
package uk.ivanc.archimvvm;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.Collections;
import java.util.List;
import uk.ivanc.archimvvm.model.Repository;
public class RepositoryAdapter extends RecyclerView.Adapter<RepositoryAdapter.RepositoryViewHolder> {
private List<Repository> repositories;
private Callback callback;
public RepositoryAdapter() {
this.repositories = Collections.emptyList();
}
public RepositoryAdapter(List<Repository> repositories) {
this.repositories = repositories;
}
public void setRepositories(List<Repository> repositories) {
this.repositories = repositories;
}
public void setCallback(Callback callback) {
this.callback = callback;
}
@Override
public RepositoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_repo, parent, false);
final RepositoryViewHolder viewHolder = new RepositoryViewHolder(itemView);
viewHolder.contentLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (callback != null) {
callback.onItemClick(viewHolder.repository);
}
}
});
return viewHolder;
}
@Override
public void onBindViewHolder(RepositoryViewHolder holder, int position) {
Repository repository = repositories.get(position);
Context context = holder.titleTextView.getContext();
holder.repository = repository;
holder.titleTextView.setText(repository.name);
holder.descriptionTextView.setText(repository.description);
holder.watchersTextView.setText(
context.getResources().getString(R.string.text_watchers, repository.watchers));
holder.starsTextView.setText(
context.getResources().getString(R.string.text_stars, repository.starts));
holder.forksTextView.setText(
context.getResources().getString(R.string.text_forks, repository.forks));
}
@Override
public int getItemCount() {
return repositories.size();
}
public static class RepositoryViewHolder extends RecyclerView.ViewHolder {
public View contentLayout;
public TextView titleTextView;
public TextView descriptionTextView;
public TextView watchersTextView;
public TextView starsTextView;
public TextView forksTextView;
public Repository repository;
public RepositoryViewHolder(View itemView) {
super(itemView);
contentLayout = itemView.findViewById(R.id.layout_content);
titleTextView = (TextView) itemView.findViewById(R.id.text_repo_title);
descriptionTextView = (TextView) itemView.findViewById(R.id.text_repo_description);
watchersTextView = (TextView) itemView.findViewById(R.id.text_watchers);
starsTextView = (TextView) itemView.findViewById(R.id.text_stars);
forksTextView = (TextView) itemView.findViewById(R.id.text_forks);
}
}
public interface Callback {
void onItemClick(Repository repository);
}
}

View File

@@ -0,0 +1,32 @@
package uk.ivanc.archimvvm.model;
import java.util.List;
import retrofit.GsonConverterFactory;
import retrofit.Retrofit;
import retrofit.RxJavaCallAdapterFactory;
import retrofit.http.GET;
import retrofit.http.Path;
import retrofit.http.Url;
import rx.Observable;
public interface GithubService {
@GET("users/{username}/repos")
Observable<List<Repository>> publicRepositories(@Path("username") String username);
@GET
Observable<User> userFromUrl(@Url String userUrl);
class Factory {
public static GithubService create() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
return retrofit.create(GithubService.class);
}
}
}

View File

@@ -0,0 +1,115 @@
package uk.ivanc.archimvvm.model;
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.annotations.SerializedName;
public class Repository implements Parcelable {
public long id;
public String name;
public String description;
public int forks;
public int watchers;
@SerializedName("stargazers_count")
public int starts;
public String language;
public String homepage;
public User owner;
public boolean fork;
public Repository() {
}
public boolean hasHomepage() {
return homepage != null && !homepage.isEmpty();
}
public boolean hasLanguage() {
return language != null && !language.isEmpty();
}
public boolean isFork() {
return fork;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(this.id);
dest.writeString(this.name);
dest.writeString(this.description);
dest.writeInt(this.forks);
dest.writeInt(this.watchers);
dest.writeInt(this.starts);
dest.writeString(this.language);
dest.writeString(this.homepage);
dest.writeParcelable(this.owner, 0);
dest.writeByte(fork ? (byte) 1 : (byte) 0);
}
protected Repository(Parcel in) {
this.id = in.readLong();
this.name = in.readString();
this.description = in.readString();
this.forks = in.readInt();
this.watchers = in.readInt();
this.starts = in.readInt();
this.language = in.readString();
this.homepage = in.readString();
this.owner = in.readParcelable(User.class.getClassLoader());
this.fork = in.readByte() != 0;
}
public static final Creator<Repository> CREATOR = new Creator<Repository>() {
public Repository createFromParcel(Parcel source) {
return new Repository(source);
}
public Repository[] newArray(int size) {
return new Repository[size];
}
};
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Repository that = (Repository) o;
if (id != that.id) return false;
if (forks != that.forks) return false;
if (watchers != that.watchers) return false;
if (starts != that.starts) return false;
if (fork != that.fork) return false;
if (name != null ? !name.equals(that.name) : that.name != null) return false;
if (description != null ? !description.equals(that.description) : that.description != null)
return false;
if (language != null ? !language.equals(that.language) : that.language != null)
return false;
if (homepage != null ? !homepage.equals(that.homepage) : that.homepage != null)
return false;
return !(owner != null ? !owner.equals(that.owner) : that.owner != null);
}
@Override
public int hashCode() {
int result = (int) (id ^ (id >>> 32));
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (description != null ? description.hashCode() : 0);
result = 31 * result + forks;
result = 31 * result + watchers;
result = 31 * result + starts;
result = 31 * result + (language != null ? language.hashCode() : 0);
result = 31 * result + (homepage != null ? homepage.hashCode() : 0);
result = 31 * result + (owner != null ? owner.hashCode() : 0);
result = 31 * result + (fork ? 1 : 0);
return result;
}
}

View File

@@ -0,0 +1,94 @@
package uk.ivanc.archimvvm.model;
import android.os.Parcel;
import android.os.Parcelable;
import com.google.gson.annotations.SerializedName;
public class User implements Parcelable {
public long id;
public String name;
public String url;
public String email;
public String login;
public String location;
@SerializedName("avatar_url")
public String avatarUrl;
public User() {
}
public boolean hasEmail() {
return email != null && !email.isEmpty();
}
public boolean hasLocation() {
return location != null && !location.isEmpty();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(this.id);
dest.writeString(this.name);
dest.writeString(this.url);
dest.writeString(this.email);
dest.writeString(this.login);
dest.writeString(this.location);
dest.writeString(this.avatarUrl);
}
protected User(Parcel in) {
this.id = in.readLong();
this.name = in.readString();
this.url = in.readString();
this.email = in.readString();
this.login = in.readString();
this.location = in.readString();
this.avatarUrl = in.readString();
}
public static final Creator<User> CREATOR = new Creator<User>() {
public User createFromParcel(Parcel source) {
return new User(source);
}
public User[] newArray(int size) {
return new User[size];
}
};
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
if (id != user.id) return false;
if (name != null ? !name.equals(user.name) : user.name != null) return false;
if (url != null ? !url.equals(user.url) : user.url != null) return false;
if (email != null ? !email.equals(user.email) : user.email != null) return false;
if (login != null ? !login.equals(user.login) : user.login != null) return false;
if (location != null ? !location.equals(user.location) : user.location != null)
return false;
return !(avatarUrl != null ? !avatarUrl.equals(user.avatarUrl) : user.avatarUrl != null);
}
@Override
public int hashCode() {
int result = (int) (id ^ (id >>> 32));
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (url != null ? url.hashCode() : 0);
result = 31 * result + (email != null ? email.hashCode() : 0);
result = 31 * result + (login != null ? login.hashCode() : 0);
result = 31 * result + (location != null ? location.hashCode() : 0);
result = 31 * result + (avatarUrl != null ? avatarUrl.hashCode() : 0);
return result;
}
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -0,0 +1,82 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/very_light_grey"
tools:context=".MainActivity">
<ImageView
android:layout_width="110dp"
android:layout_height="110dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:src="@drawable/octocat" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
<RelativeLayout
android:id="@+id/layout_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/toolbar"
android:background="?attr/colorPrimary"
android:paddingBottom="20dp"
android:paddingLeft="@dimen/vertical_margin"
android:paddingRight="@dimen/vertical_margin">
<EditText
android:id="@+id/edit_text_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/hit_username"
android:imeOptions="actionSearch"
android:inputType="text"
android:textColor="@color/white"
android:theme="@style/LightEditText" />
</RelativeLayout>
<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/layout_search"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:visibility="gone" />
<TextView
android:id="@+id/text_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/layout_search"
android:layout_centerHorizontal="true"
android:layout_marginLeft="@dimen/vertical_margin"
android:layout_marginRight="@dimen/vertical_margin"
android:layout_marginTop="20dp"
android:gravity="center"
android:text="@string/default_info_message"
android:textColor="@color/secondary_text"
android:textSize="18sp" />
<android.support.v7.widget.RecyclerView
android:id="@+id/repos_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/layout_search"
android:clipToPadding="false"
android:paddingBottom="@dimen/vertical_margin_half"
android:paddingTop="@dimen/vertical_margin_half"
android:scrollbars="vertical"
android:visibility="gone"
tools:listitem="@layout/item_repo" />
</RelativeLayout>

View File

@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:activity=".RepositoryActivity">
<ImageView
android:layout_width="110dp"
android:layout_height="110dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:src="@drawable/octocat" />
<LinearLayout
android:id="@+id/layout_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
<TextView
android:id="@+id/text_repo_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="10dp"
android:textColor="@color/white"
android:textSize="16sp"
tools:text="This is the description for this repository that can be fairly long" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/layout_header"
android:layout_marginBottom="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:orientation="vertical">
<TextView
android:id="@+id/text_homepage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoLink="web"
android:textSize="16sp"
tools:text="http://google.com/library" />
<TextView
android:id="@+id/text_language"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textColor="@color/secondary_text"
android:textSize="16sp"
tools:text="Language: Java" />
<TextView
android:id="@+id/text_fork"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/text_fork"
android:textColor="@color/secondary_text"
android:textSize="16sp" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="16dp"
android:background="@color/divider" />
<RelativeLayout
android:id="@+id/layout_owner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:orientation="horizontal"
android:visibility="gone"
tools:visibility="visible">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/image_owner"
android:layout_width="65dp"
android:layout_height="65dp" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/image_owner"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:layout_centerVertical="true"
android:orientation="vertical">
<TextView
android:id="@+id/text_owner_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/secondary_text"
android:textSize="16sp"
tools:text="Owner Name" />
<TextView
android:id="@+id/text_owner_email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:textColor="@color/secondary_text"
android:textSize="14sp"
tools:text="owner@email.com" />
<TextView
android:id="@+id/text_owner_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginTop="3dp"
android:textColor="@color/secondary_text"
android:textSize="14sp"
tools:text="Brighton" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
</RelativeLayout>

View File

@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/vertical_margin_half"
android:layout_marginLeft="@dimen/vertical_margin"
android:layout_marginRight="@dimen/vertical_margin"
android:layout_marginTop="@dimen/vertical_margin_half"
card_view:cardCornerRadius="2dp">
<LinearLayout
android:id="@+id/layout_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
android:orientation="vertical">
<TextView
android:id="@+id/text_repo_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:paddingTop="12dp"
android:textSize="20sp"
tools:text="Repository Name" />
<TextView
android:id="@+id/text_repo_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="12dp"
android:paddingLeft="12dp"
android:paddingRight="12dp"
android:paddingTop="10dp"
android:textSize="14sp"
android:textColor="@color/secondary_text"
tools:text="This is where the repository description will go" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/divider" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="horizontal">
<TextView
android:id="@+id/text_watchers"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:textColor="@color/secondary_text"
tools:text="10 \nWatchers" />
<TextView
android:id="@+id/text_stars"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:textColor="@color/secondary_text"
tools:text="230 \nStars" />
<TextView
android:id="@+id/text_forks"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:textColor="@color/secondary_text"
tools:text="0 \nForks" />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#000000</color>
<color name="white">#ffffff</color>
<color name="white_transparent">#75ffffff</color>
<color name="very_light_grey">#e1e1e1</color>
<color name="primary">#3F51B5</color>
<color name="primary_dark">#303F9F</color>
<color name="primary_light">#C5CAE9</color>
<color name="accent">#03A9F4</color>
<color name="primary_text">#212121</color>
<color name="secondary_text">#727272</color>
<color name="icons">#FFFFFF</color>
<color name="divider">#cbcbcb</color>
</resources>

View File

@@ -0,0 +1,7 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="horizontal_margin">12dp</dimen>
<dimen name="vertical_margin">12dp</dimen>
<dimen name="horizontal_margin_half">6dp</dimen>
<dimen name="vertical_margin_half">6dp</dimen>
</resources>

View File

@@ -0,0 +1,16 @@
<resources>
<string name="app_name">Archi MVVM</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
<string name="text_stars">%d \nStars</string>
<string name="text_watchers">%d \nWatchers</string>
<string name="text_forks">%d \nForks</string>
<string name="error_loading_repos">Oops, something went wrong</string>
<string name="text_empty_repos">This account doesn\'t have any public repository</string>
<string name="error_username_not_found">Oops, Octocat doesn\'t know that username</string>
<string name="hit_username">GitHub username</string>
<string name="default_info_message">Enter a GitHub username above to see its repositories</string>
<string name="text_fork">This repository is a fork</string>
<string name="text_language">Language: %s</string>
</resources>

View File

@@ -0,0 +1,24 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primary_dark</item>
<item name="colorAccent">@color/accent</item>
<item name="android:textColor">@color/primary_text</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowNoTitle">true</item>
<item name="windowActionBar">false</item>
</style>
<style name="LightEditText">
<item name="colorControlNormal">@color/white_transparent</item>
<item name="colorControlActivated">@color/white</item>
<item name="colorControlHighlight">@color/white</item>
<item name="android:textColor">@color/white</item>
<item name="android:textColorHint">@color/white_transparent</item>
</style>
</resources>

View File

@@ -1 +1 @@
include ':app', ':app-mvp'
include ':app', ':app-mvp', ':app-mvvm'