Module Detail Overview Implementation #2

This commit is contained in:
Caesar2011
2018-06-06 14:45:43 +02:00
parent c45d859a51
commit 5c4bd50379
37 changed files with 457 additions and 528 deletions

Binary file not shown.

View File

@@ -34,7 +34,7 @@ dependencies {
})
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:design:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.1'
implementation 'com.android.volley:volley:1.0.0'
//noinspection GradleDependency
implementation 'com.google.android.gms:play-services-auth:15.0.0'
@@ -45,5 +45,6 @@ dependencies {
implementation 'org.jbundle.util.osgi.wrapped:org.jbundle.util.osgi.wrapped.org.apache.http.client:4.1.2'
implementation 'com.android.support:support-v4:27.1.1'
implementation 'org.jetbrains:annotations-java5:15.0'
implementation 'com.ms-square:expandableTextView:0.1.4'
implementation files('libs/jericho-html-3.4.jar')
}

View File

@@ -16,8 +16,8 @@
android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

View File

@@ -22,17 +22,11 @@ import de.sebse.fuplanner.fragments.LoginFragment;
import de.sebse.fuplanner.fragments.moddetails.ModDetailFragment;
import de.sebse.fuplanner.fragments.ModulesFragment;
import de.sebse.fuplanner.fragments.StartupFragment;
import de.sebse.fuplanner.services.GoogleAuth.ConnectedListener;
import de.sebse.fuplanner.services.GoogleAuth.Credentials;
import de.sebse.fuplanner.services.GoogleAuth.CredentialsListener;
import de.sebse.fuplanner.services.GoogleAuth.GoogleAuth;
import de.sebse.fuplanner.services.KVV.KVV;
import de.sebse.fuplanner.services.KVV.LoginToken;
import de.sebse.fuplanner.services.KVV.Modules;
import de.sebse.fuplanner.tools.logging.Logger;
import de.sebse.fuplanner.tools.network.NetworkCallback;
import de.sebse.fuplanner.tools.network.NetworkError;
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener,
@@ -55,7 +49,7 @@ public class MainActivity extends AppCompatActivity
DrawerLayout drawer = findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.setDrawerListener(toggle);
drawer.addDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = findViewById(R.id.nav_view);
@@ -75,35 +69,23 @@ public class MainActivity extends AppCompatActivity
findViewById(R.id.app_bar_layout).setVisibility(View.GONE);
this.getGoogleAuth().connect(new ConnectedListener() {
@Override
public void connected() {
// getGoogleAuth().setLoginState("seedorf96", "m&gcwBaT@");
getGoogleAuth().getLoginState(new CredentialsListener() {
@Override
public void onCredentials(final Credentials credentials) {
if (credentials == null || credentials.getUsername() == null || credentials.getPassword() == null) {
MainActivity.this.getKVV().endUpdate();
changeLoginState(null);
return;
}
MainActivity.this.getKVV().login(credentials.getUsername(), credentials.getPassword(), new NetworkCallback<LoginToken>() {
@Override
public void onResponse(@NonNull LoginToken success) {
MainActivity.this.getKVV().endUpdate();
changeLoginState(credentials);
}
}, new NetworkErrorCallback() {
@Override
public void onError(NetworkError error) {
log.e(error);
MainActivity.this.getKVV().endUpdate();
changeLoginState(null);
}
});
}
this.getGoogleAuth().connect(() -> {
// getGoogleAuth().setLoginState("seedorf96", "m&gcwBaT@");
getGoogleAuth().getLoginState(credentials -> {
if (credentials == null || credentials.getUsername() == null || credentials.getPassword() == null) {
MainActivity.this.getKVV().endUpdate();
changeLoginState(null);
return;
}
MainActivity.this.getKVV().login(credentials.getUsername(), credentials.getPassword(), success -> {
MainActivity.this.getKVV().endUpdate();
changeLoginState(credentials);
}, error -> {
log.e(error);
MainActivity.this.getKVV().endUpdate();
changeLoginState(null);
});
}
});
});
}
@@ -159,14 +141,11 @@ public class MainActivity extends AppCompatActivity
break;
case R.id.nav_logout:
this.getKVV().logout();
this.getGoogleAuth().getLoginState(new CredentialsListener() {
@Override
public void onCredentials(Credentials credentials) {
if (credentials != null) {
MainActivity.this.getGoogleAuth().deleteLoginState(credentials.getUsername(), credentials.getPassword());
}
MainActivity.this.changeLoginState(null);
this.getGoogleAuth().getLoginState(credentials -> {
if (credentials != null) {
MainActivity.this.getGoogleAuth().deleteLoginState(credentials.getUsername(), credentials.getPassword());
}
MainActivity.this.changeLoginState(null);
});
break;
}
@@ -236,29 +215,21 @@ public class MainActivity extends AppCompatActivity
navigationView.getMenu().clear();
navigationView.inflateMenu(R.menu.activity_main_drawer_login);
navigationView.setCheckedItem(R.id.nav_modules);
getKVV().getModuleList(new NetworkCallback<Modules>() {
@Override
public void onResponse(@NonNull Modules success) {
log.d("Modules.get", success.size());
//SubMenu moduleMenu = navigationView.getMenu().findItem(R.id.nav_modules).getSubMenu();
int i = 0;
for (Iterator<Modules.Module> it = success.latestSemesterIterator(); it.hasNext(); ) {
Modules.Module module = it.next();
MenuItem menuItem = navigationView.getMenu().add(Menu.NONE, Menu.NONE, 101 + i, module.title);
final int finalI = i;
menuItem.setOnMenuItemClickListener(item -> {
onModulesFragmentInteraction(finalI);
return false;
});
i++;
}
getKVV().getModuleList(success -> {
log.d("Modules.get", success.size());
//SubMenu moduleMenu = navigationView.getMenu().findItem(R.id.nav_modules).getSubMenu();
int i = 0;
for (Iterator<Modules.Module> it = success.latestSemesterIterator(); it.hasNext(); ) {
Modules.Module module = it.next();
MenuItem menuItem = navigationView.getMenu().add(Menu.NONE, Menu.NONE, 101 + i, module.title);
final int finalI = i;
menuItem.setOnMenuItemClickListener(item -> {
onModulesFragmentInteraction(finalI);
return false;
});
i++;
}
}, new NetworkErrorCallback() {
@Override
public void onError(NetworkError error) {
log.d("Modules.error", error);
}
});
}, error -> log.d("Modules.error", error));
}
}
@@ -280,16 +251,8 @@ public class MainActivity extends AppCompatActivity
@Override
public void onModulesFragmentInteraction(final int itemPosition) {
log.d("Item clicked", itemPosition);
getKVV().getModuleList(new NetworkCallback<Modules>() {
@Override
public void onResponse(@NonNull Modules success) {
log.d(success.get(itemPosition).title);
}
}, new NetworkErrorCallback() {
@Override
public void onError(NetworkError error) {
// TODO
}
getKVV().getModuleList(success -> log.d(success.get(itemPosition).title), error -> {
// TODO
});
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

View File

@@ -5,7 +5,6 @@ import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -16,11 +15,7 @@ import de.sebse.fuplanner.R;
import de.sebse.fuplanner.services.GoogleAuth.Credentials;
import de.sebse.fuplanner.services.GoogleAuth.GoogleAuth;
import de.sebse.fuplanner.services.KVV.KVV;
import de.sebse.fuplanner.services.KVV.LoginToken;
import de.sebse.fuplanner.tools.logging.Logger;
import de.sebse.fuplanner.tools.network.NetworkCallback;
import de.sebse.fuplanner.tools.network.NetworkError;
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
/**
* A simple {@link Fragment} subclass.
@@ -63,54 +58,45 @@ public class LoginFragment extends Fragment {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_login, container, false);
View btn_login = v.findViewById(R.id.btn_login);
if (btn_login != null) {
btn_login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final ProgressDialog progressDialog = new ProgressDialog(LoginFragment.this.getContext(),
R.style.FUTheme_Dialog);
progressDialog.setIndeterminate(true);
progressDialog.setMessage("Authenticating...");
progressDialog.show();
btn_login.setOnClickListener(view -> {
final ProgressDialog progressDialog = new ProgressDialog(LoginFragment.this.getContext(),
R.style.FUTheme_Dialog);
progressDialog.setIndeterminate(true);
progressDialog.setMessage("Authenticating...");
progressDialog.show();
EditText input_usr = ((View) view.getParent()).findViewById(R.id.input_username);
EditText input_pwd = ((View) view.getParent()).findViewById(R.id.input_password);
if (input_usr != null) {
if (input_pwd != null) {
if (LoginFragment.this.getActivity() == null) {
log.e("Login fragment has no activity!");
return;
}
final String username = input_usr.getText().toString();
final String password = input_pwd.getText().toString();
final KVV kvv = ((MainActivity) LoginFragment.this.getActivity()).getKVV();
final GoogleAuth gauth = ((MainActivity) LoginFragment.this.getActivity()).getGoogleAuth();
kvv.login(username, password, new NetworkCallback<LoginToken>() {
@Override
public void onResponse(@NonNull LoginToken success) {
progressDialog.dismiss();
log.d("success", success.toString());
gauth.setLoginState(username, password);
if (LoginFragment.this.mListener != null) {
Credentials cred = new Credentials(username, password);
LoginFragment.this.mListener.onLoginFragmentInteraction(cred);
}
}
}, new NetworkErrorCallback() {
@Override
public void onError(NetworkError error) {
progressDialog.dismiss();
log.e("error", error);
}
});
EditText input_usr = ((View) view.getParent()).findViewById(R.id.input_username);
EditText input_pwd = ((View) view.getParent()).findViewById(R.id.input_password);
if (input_usr != null) {
if (input_pwd != null) {
if (LoginFragment.this.getActivity() == null) {
log.e("Login fragment has no activity!");
return;
}
final String username = input_usr.getText().toString();
final String password = input_pwd.getText().toString();
final KVV kvv = ((MainActivity) LoginFragment.this.getActivity()).getKVV();
final GoogleAuth gauth = ((MainActivity) LoginFragment.this.getActivity()).getGoogleAuth();
kvv.login(username, password, success -> {
progressDialog.dismiss();
log.d("success", success.toString());
gauth.setLoginState(username, password);
if (LoginFragment.this.mListener != null) {
Credentials cred = new Credentials(username, password);
LoginFragment.this.mListener.onLoginFragmentInteraction(cred);
}
}, error -> {
progressDialog.dismiss();
log.e("error", error);
});
}
}
});

View File

@@ -13,11 +13,7 @@ import android.view.ViewGroup;
import de.sebse.fuplanner.MainActivity;
import de.sebse.fuplanner.R;
import de.sebse.fuplanner.services.KVV.Modules;
import de.sebse.fuplanner.tools.logging.Logger;
import de.sebse.fuplanner.tools.network.NetworkCallback;
import de.sebse.fuplanner.tools.network.NetworkError;
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
/**
* A fragment representing a list of Items.
@@ -75,18 +71,10 @@ public class ModulesFragment extends Fragment {
if (getActivity() != null) {
((MainActivity) getActivity()).getKVV().getModuleList(new NetworkCallback<Modules>() {
@Override
public void onResponse(@NonNull Modules success) {
log.d(success);
adapter.setModules(success);
}
}, new NetworkErrorCallback() {
@Override
public void onError(NetworkError error) {
Log.e("ModFrag", error.toString());
}
});
((MainActivity) getActivity()).getKVV().getModuleList(success -> {
log.d(success);
adapter.setModules(success);
}, error -> Log.e("ModFrag", error.toString()));
}
}
return view;

View File

@@ -1,5 +1,6 @@
package de.sebse.fuplanner.fragments;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
@@ -31,29 +32,28 @@ public class ModulesRecyclerViewAdapter extends RecyclerView.Adapter<ModulesRecy
this.notifyDataSetChanged();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.fragment_modules, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
if (mValues == null)
return;
position = holder.getAdapterPosition();
holder.mItemPosition = position;
holder.mIdView.setText(mValues.get(position).semester);
holder.mContentView.setText(mValues.get(position).title);
holder.mView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (null != mListener) {
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
mListener.onModulesFragmentInteraction(holder.mItemPosition);
}
holder.mView.setOnClickListener(v -> {
if (null != mListener) {
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
mListener.onModulesFragmentInteraction(holder.mItemPosition);
}
});
}
@@ -77,8 +77,8 @@ public class ModulesRecyclerViewAdapter extends RecyclerView.Adapter<ModulesRecy
public ViewHolder(View view) {
super(view);
mView = view;
mIdView = (TextView) view.findViewById(R.id.id);
mContentView = (TextView) view.findViewById(R.id.content);
mIdView = view.findViewById(R.id.id);
mContentView = view.findViewById(R.id.content);
}
@Override

View File

@@ -1,7 +1,5 @@
package de.sebse.fuplanner.fragments;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
@@ -13,9 +11,6 @@ import de.sebse.fuplanner.R;
/**
* A simple {@link Fragment} subclass.
* Activities that contain this fragment must implement the
* {@link StartupFragment.OnStartupFragmentInteractionListener} interface
* to handle interaction events.
* Use the {@link StartupFragment#newInstance} factory method to
* create an instance of this fragment.
*/

View File

@@ -1,7 +1,6 @@
package de.sebse.fuplanner.fragments.moddetails;
import android.content.Context;
import android.content.res.Resources;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;

View File

@@ -2,6 +2,7 @@ package de.sebse.fuplanner.fragments.moddetails;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
@@ -54,7 +55,7 @@ public class ModDetailAnnounceFragment extends Fragment {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_mod_detail_announce, container, false);

View File

@@ -31,7 +31,6 @@ public class ModDetailFragment extends Fragment {
private int mItemPos;
private OnModuleDetailFragmentInteractionListener mListener;
private ModDetailAdapter adapterViewPager;
private Logger log = new Logger(this);
public ModDetailFragment() {
@@ -71,8 +70,8 @@ public class ModDetailFragment extends Fragment {
log.d("on create view");
View v = inflater.inflate(R.layout.fragment_mod_detail, container, false);
ViewPager vpPager = (ViewPager) v.findViewById(R.id.vpPager);
adapterViewPager = new ModDetailAdapter(getFragmentManager(), mItemPos, getContext());
ViewPager vpPager = v.findViewById(R.id.vpPager);
ModDetailAdapter adapterViewPager = new ModDetailAdapter(getFragmentManager(), mItemPos, getContext());
vpPager.setAdapter(adapterViewPager);
return v;
}

View File

@@ -1,26 +1,27 @@
package de.sebse.fuplanner.fragments.moddetails;
import android.icu.text.DateFormat;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.Date;
import java.util.Locale;
import com.ms.square.android.expandabletextview.ExpandableTextView;
import de.sebse.fuplanner.R;
import de.sebse.fuplanner.fragments.ModulesFragment;
import de.sebse.fuplanner.fragments.ModulesRecyclerViewAdapter;
import de.sebse.fuplanner.services.KVV.Announcement;
import de.sebse.fuplanner.services.KVV.Assignment;
import de.sebse.fuplanner.services.KVV.Modules;
import de.sebse.fuplanner.tools.Conversion;
import de.sebse.fuplanner.tools.ui.CustomViewHolder;
public class ModDetailOverviewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_DESCRIPTION = 1;
private static final int TYPE_ITEM = 2;
private static final int TYPE_SHOW_MORE = 3;
private Modules.Module mValue;
//private final ModulesFragment.OnModulesFragmentInteractionListener mListener;
@@ -39,18 +40,22 @@ public class ModDetailOverviewAdapter extends RecyclerView.Adapter<RecyclerView.
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view;
switch (viewType) {
case 0:
case TYPE_HEADER:
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_all_caption, parent, false);
return new HeaderViewHolder(view);
case 1:
case TYPE_DESCRIPTION:
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_moddetails_description, parent, false);
return new DescriptionViewHolder(view);
case 2:
case TYPE_ITEM:
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_moddetails_announcements, parent, false);
return new AnnounceViewHolder(view);
return new ItemViewHolder(view);
case TYPE_SHOW_MORE:
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_all_show_more, parent, false);
return new CustomViewHolder(view);
default:
//noinspection ConstantConditions
return null;
@@ -62,39 +67,61 @@ public class ModDetailOverviewAdapter extends RecyclerView.Adapter<RecyclerView.
// Just as an example, return 0 or 2 depending on position
// Note that unlike in ListView adapters, types don't have to be contiguous
if (position < 3)
return position % 2;
else
return 2;
return position % 2; // HEADER, DESCRIPTION, HEADER
int count = 2;
// Announcements
if (position <= count+getAnnounceCount(2))
return TYPE_ITEM;
count += getAnnounceCount(3);
if (position <= count)
return TYPE_SHOW_MORE;
// Assignments
count++;
if (position <= count)
return TYPE_HEADER;
count++;
if (position <= count+getAssignmentCount(2))
return TYPE_ITEM;
count += getAssignmentCount(3);
if (position <= count)
return TYPE_SHOW_MORE;
return 0;
}
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder holder, int position) {
if (mValue == null)
return;
int announce_count = getAnnounceCount(2);
if (position == 0) {
HeaderViewHolder h = (HeaderViewHolder) holder;
h.mCaption.setText(h.mView.getResources().getText(R.string.description));
h.mCaption.setText(R.string.description);
} else if (position == 1) {
DescriptionViewHolder d = (DescriptionViewHolder) holder;
d.mText.setText(mValue.description);
//d.mText.setText(d.mView.getResources().getText(R.string.app_name));
} else if (position == 2) {
HeaderViewHolder h = (HeaderViewHolder) holder;
h.mCaption.setText(h.mView.getResources().getString(R.string.announcements_count, getAnnounceCount()));
} else if (position <= announce_count+2) {
AnnounceViewHolder a = (AnnounceViewHolder) holder;
} else if (position <= getAnnounceCount(2)+2) {
ItemViewHolder a = (ItemViewHolder) holder;
Announcement announce = mValue.announcements.get(position - 3);
a.mTitle.setText(announce.getTitle());
a.mPublisher.setText(announce.getCreatedBy());
a.mDate.setText(Conversion.getModifiedDate(announce.getCreatedOn()));
a.mSubLeft.setText(announce.getCreatedBy());
a.mSubRight.setText(Conversion.getModifiedDate(announce.getCreatedOn()));
} else if (position <= getAnnounceCount(3)+getAssignmentCount(2)+3) {
ItemViewHolder a = (ItemViewHolder) holder;
Assignment assignment = mValue.assignments.get(position - 3);
a.mTitle.setText(assignment.getTitle());
a.mSubLeft.setText(assignment.getStatus());
a.mSubRight.setText(Conversion.getModifiedDate(assignment.getDueDate()));
}
}
@Override
public int getItemCount() {
if (mValue != null) {
return 3+getAnnounceCount();
return 4+getAnnounceCount(3)+getAssignmentCount(3);
}
return 0;
}
@@ -109,6 +136,16 @@ public class ModDetailOverviewAdapter extends RecyclerView.Adapter<RecyclerView.
return Math.min(max, getAnnounceCount());
}
private int getAssignmentCount() {
if (mValue.assignments != null)
return mValue.assignments.size();
return 0;
}
private int getAssignmentCount(int max) {
return Math.min(max, getAssignmentCount());
}
@@ -131,11 +168,11 @@ public class ModDetailOverviewAdapter extends RecyclerView.Adapter<RecyclerView.
}
public class DescriptionViewHolder extends CustomViewHolder {
final TextView mText;
final ExpandableTextView mText;
DescriptionViewHolder(View view) {
super(view);
mText = view.findViewById(R.id.description);
mText = view.findViewById(R.id.expand_text_view);//.findViewById(R.id.description);
}
@Override
@@ -145,21 +182,21 @@ public class ModDetailOverviewAdapter extends RecyclerView.Adapter<RecyclerView.
}
public class AnnounceViewHolder extends CustomViewHolder {
public class ItemViewHolder extends CustomViewHolder {
final TextView mTitle;
final TextView mPublisher;
final TextView mDate;
final TextView mSubLeft;
final TextView mSubRight;
AnnounceViewHolder(View view) {
ItemViewHolder(View view) {
super(view);
mTitle = view.findViewById(R.id.title);
mPublisher = view.findViewById(R.id.publisher);
mDate = view.findViewById(R.id.date);
mSubLeft = view.findViewById(R.id.sub_left);
mSubRight = view.findViewById(R.id.sub_right);
}
@Override
public String toString() {
return super.toString() + " '" + mTitle.getText() + "' '" + mPublisher.getText() + "' '" + mDate.getText() + "'";
return super.toString() + " '" + mTitle.getText() + "' '" + mSubLeft.getText() + "' '" + mSubRight.getText() + "'";
}
}
}

View File

@@ -7,20 +7,15 @@ import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import de.sebse.fuplanner.MainActivity;
import de.sebse.fuplanner.R;
import de.sebse.fuplanner.fragments.ModulesRecyclerViewAdapter;
import de.sebse.fuplanner.services.KVV.KVV;
import de.sebse.fuplanner.services.KVV.Modules;
import de.sebse.fuplanner.tools.logging.Logger;
import de.sebse.fuplanner.tools.network.NetworkCallback;
import de.sebse.fuplanner.tools.network.NetworkError;
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
/**
* A simple {@link Fragment} subclass.
@@ -31,6 +26,7 @@ public class ModDetailOverviewFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_POSITION = "itemPosition";
private static Logger slog = new Logger(ModDetailOverviewFragment.class);
// TODO: Rename and change types of parameters
private int mItemPos;
@@ -54,8 +50,7 @@ public class ModDetailOverviewFragment extends Fragment {
Bundle args = new Bundle();
args.putInt(ARG_POSITION, itemPosition);
fragment.setArguments(args);
String s = "ModDetailOverviewFragment".toString();
Log.d(s, "new Instance");
slog.d("new Instance");
return fragment;
}

View File

@@ -5,5 +5,5 @@ package de.sebse.fuplanner.services.GoogleAuth;
*/
public interface ConnectedListener {
public void connected();
void connected();
}

View File

@@ -5,5 +5,5 @@ package de.sebse.fuplanner.services.GoogleAuth;
*/
public interface CredentialsListener {
public void onCredentials(Credentials credentials);
void onCredentials(Credentials credentials);
}

View File

@@ -2,7 +2,6 @@ package de.sebse.fuplanner.services.GoogleAuth;
import android.content.Intent;
import android.content.IntentSender;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
@@ -10,7 +9,6 @@ import android.widget.Toast;
import com.google.android.gms.auth.api.credentials.Credential;
import com.google.android.gms.auth.api.credentials.CredentialRequest;
import com.google.android.gms.auth.api.credentials.CredentialRequestResponse;
import com.google.android.gms.auth.api.credentials.CredentialsClient;
import com.google.android.gms.auth.api.credentials.CredentialsOptions;
import com.google.android.gms.common.ConnectionResult;
@@ -18,8 +16,6 @@ import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.ResolvableApiException;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import static android.app.Activity.RESULT_OK;
@@ -65,38 +61,35 @@ public class GoogleAuth {
.build();
mCredentialsClient.request(request).addOnCompleteListener(new OnCompleteListener<CredentialRequestResponse>() {
@Override
public void onComplete(@NonNull Task<CredentialRequestResponse> task) {
if (task.isSuccessful()) {
// Successfully read the credential without any user interaction, this
// means there was only a single credential and the user has auto
// sign-in enabled.
Credential credential = task.getResult().getCredential();
credentialsListener.onCredentials(new Credentials(credential.getId(), credential.getPassword()));
return;
}
mCredentialsClient.request(request).addOnCompleteListener(task -> {
if (task.isSuccessful()) {
// Successfully read the credential without any user interaction, this
// means there was only a single credential and the user has auto
// sign-in enabled.
Credential credential = task.getResult().getCredential();
credentialsListener.onCredentials(new Credentials(credential.getId(), credential.getPassword()));
return;
}
Exception e = task.getException();
if (e instanceof ResolvableApiException) {
// This is most likely the case where the user has multiple saved
// credentials and needs to pick one. This requires showing UI to
// resolve the read request.
GoogleAuth.this.mCredentialsListener = credentialsListener;
ResolvableApiException rae = (ResolvableApiException) e;
resolveResult(rae, RequestCode.RC_READ);
return;
}
Exception e = task.getException();
if (e instanceof ResolvableApiException) {
// This is most likely the case where the user has multiple saved
// credentials and needs to pick one. This requires showing UI to
// resolve the read request.
GoogleAuth.this.mCredentialsListener = credentialsListener;
ResolvableApiException rae = (ResolvableApiException) e;
resolveResult(rae, RequestCode.RC_READ);
return;
}
if (e instanceof ApiException) {
ApiException ae = (ApiException) e;
if (ae.getStatusCode() == CommonStatusCodes.SIGN_IN_REQUIRED) {
// This means only a hint is available, but we are handling that
// elsewhere so no need to act here.
} else {
credentialsListener.onCredentials(null);
Log.w(TAG, "Unexpected status code: " + ae.getStatusCode());
}
if (e instanceof ApiException) {
ApiException ae = (ApiException) e;
if (ae.getStatusCode() == CommonStatusCodes.SIGN_IN_REQUIRED) {
// This means only a hint is available, but we are handling that
// elsewhere so no need to act here.
} else {
credentialsListener.onCredentials(null);
Log.w(TAG, "Unexpected status code: " + ae.getStatusCode());
}
}
});
@@ -111,25 +104,22 @@ public class GoogleAuth {
Credential credential = new Credential.Builder(username)
.setPassword(password)
.build();
mCredentialsClient.save(credential).addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
showToast("Credential saved.");
return;
}
mCredentialsClient.save(credential).addOnCompleteListener(task -> {
if (task.isSuccessful()) {
showToast("Credential saved.");
return;
}
Exception e = task.getException();
if (e instanceof ResolvableApiException) {
// The first time a credential is saved, the user is shown UI
// to confirm the action. This requires resolution.
ResolvableApiException rae = (ResolvableApiException) e;
resolveResult(rae, RequestCode.RC_SAVE);
} else {
// Save failure cannot be resolved.
Log.w(TAG, "Save failed.", e);
showToast("Credential Save Failed");
}
Exception e = task.getException();
if (e instanceof ResolvableApiException) {
// The first time a credential is saved, the user is shown UI
// to confirm the action. This requires resolution.
ResolvableApiException rae = (ResolvableApiException) e;
resolveResult(rae, RequestCode.RC_SAVE);
} else {
// Save failure cannot be resolved.
Log.w(TAG, "Save failed.", e);
showToast("Credential Save Failed");
}
});
}
@@ -142,12 +132,9 @@ public class GoogleAuth {
Credential credential = new Credential.Builder(username)
.setPassword(password)
.build();
mCredentialsClient.delete(credential).addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
// Credential was deleted successfully
}
mCredentialsClient.delete(credential).addOnCompleteListener(task -> {
if (task.isSuccessful()) {
// Credential was deleted successfully
}
});
}

View File

@@ -1,12 +1,8 @@
package de.sebse.fuplanner.services.KVV;
import android.content.Context;
import android.support.annotation.NonNull;
import android.util.Log;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -15,7 +11,6 @@ import de.sebse.fuplanner.tools.network.HTTPService;
import de.sebse.fuplanner.tools.network.NetworkCallback;
import de.sebse.fuplanner.tools.network.NetworkError;
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
import de.sebse.fuplanner.tools.network.Result;
/**
* Created by sebastian on 24.10.17.
@@ -53,29 +48,21 @@ class KVVLogin extends HTTPService {
-> JSESSIONID 5c10406f-588c-4c16-96e9-c80d115417de.tomcat1
*/
private void startKVVSession(final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
get("https://kvv.imp.fu-berlin.de/portal/login", null, new Response.Listener<Result>() {
@Override
public void onResponse(Result response) {
String cookies = response.getHeaders().get("Set-Cookie");
if (cookies==null) {
errorCallback.onError(new NetworkError(100101, -1, "Error on starting KVV session!"));
return;
}
HashMap<String, String> object;
try {
object = getCookie(cookies, new String[]{"JSESSIONID"});
} catch (NoSuchFieldException e) {
errorCallback.onError(new NetworkError(100102, -1, "Error on starting KVV session!"));
return;
}
callback.onResponse(object);
get("https://kvv.imp.fu-berlin.de/portal/login", null, response -> {
String cookies = response.getHeaders().get("Set-Cookie");
if (cookies==null) {
errorCallback.onError(new NetworkError(100101, -1, "Error on starting KVV session!"));
return;
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
errorCallback.onError(new NetworkError(100100, error.networkResponse.statusCode, "Error on starting KVV session!"));
HashMap<String, String> object;
try {
object = getCookie(cookies, new String[]{"JSESSIONID"});
} catch (NoSuchFieldException e) {
errorCallback.onError(new NetworkError(100102, -1, "Error on starting KVV session!"));
return;
}
});
callback.onResponse(object);
}, error -> errorCallback.onError(new NetworkError(100100, error.networkResponse.statusCode, "Error on starting KVV session!")));
}
/*
@@ -88,24 +75,16 @@ class KVVLogin extends HTTPService {
private void getSAMLRequest(String JSESSIONID, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
HashMap<String, String> cookies = new HashMap<>();
cookies.put("JSESSIONID", JSESSIONID);
get("https://kvv.imp.fu-berlin.de/sakai-login-tool/container", cookies, new Response.Listener<Result>() {
@Override
public void onResponse(Result response) {
String location = response.getHeaders().get("Location");
if (location==null) {
errorCallback.onError(new NetworkError(100111, -1, "Error on getting SAML request!"));
return;
}
HashMap<String, String> object = new HashMap<>();
object.put("Location", location);
callback.onResponse(object);
get("https://kvv.imp.fu-berlin.de/sakai-login-tool/container", cookies, response -> {
String location = response.getHeaders().get("Location");
if (location==null) {
errorCallback.onError(new NetworkError(100111, -1, "Error on getting SAML request!"));
return;
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
errorCallback.onError(new NetworkError(100110, error.networkResponse.statusCode, "Error on getting SAML request!"));
}
});
HashMap<String, String> object = new HashMap<>();
object.put("Location", location);
callback.onResponse(object);
}, error -> errorCallback.onError(new NetworkError(100110, error.networkResponse.statusCode, "Error on getting SAML request!")));
}
/*
@@ -117,29 +96,21 @@ class KVVLogin extends HTTPService {
-> ROUTEID .1
*/
private void startIdentSession(String url, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
get(url, null, new Response.Listener<Result>() {
@Override
public void onResponse(Result response) {
String cookies = response.getHeaders().get("Set-Cookie");
if (cookies==null) {
errorCallback.onError(new NetworkError(100121, -1, "Error on starting Ident session!"));
return;
}
HashMap<String, String> object;
try {
object = getCookie(cookies, new String[]{"JSESSIONID", "_idp_authn_lc_key", "ROUTEID"});
} catch (NoSuchFieldException e) {
errorCallback.onError(new NetworkError(100122, -1, "Error on starting Ident session!"));
return;
}
callback.onResponse(object);
get(url, null, response -> {
String cookies = response.getHeaders().get("Set-Cookie");
if (cookies==null) {
errorCallback.onError(new NetworkError(100121, -1, "Error on starting Ident session!"));
return;
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
errorCallback.onError(new NetworkError(100120, error.networkResponse.statusCode, "Error on starting Ident session!"));
HashMap<String, String> object;
try {
object = getCookie(cookies, new String[]{"JSESSIONID", "_idp_authn_lc_key", "ROUTEID"});
} catch (NoSuchFieldException e) {
errorCallback.onError(new NetworkError(100122, -1, "Error on starting Ident session!"));
return;
}
});
callback.onResponse(object);
}, error -> errorCallback.onError(new NetworkError(100120, error.networkResponse.statusCode, "Error on starting Ident session!")));
}
/*
@@ -160,34 +131,26 @@ class KVVLogin extends HTTPService {
HashMap<String, String> body = new HashMap<>();
body.put("j_username", username);
body.put("j_password", password);
post("https://identity.fu-berlin.de/idp-fub/Authn/UserPassword", cookies, body, new Response.Listener<Result>() {
@Override
public void onResponse(Result response) {
if (first) {
callback.onResponse(new HashMap<String, String>());
return;
}
post("https://identity.fu-berlin.de/idp-fub/Authn/UserPassword", cookies, body, response -> {
if (first) {
callback.onResponse(new HashMap<>());
return;
}
String cookies = response.getHeaders().get("Set-Cookie");
if (cookies==null) {
errorCallback.onError(new NetworkError(100131, -1, "Error on logging in to Identity Server!"));
return;
}
HashMap<String, String> object;
try {
object = getCookie(cookies, new String[]{"_idp_session"});
} catch (NoSuchFieldException e) {
errorCallback.onError(new NetworkError(100132, -1, "Error on logging in to Identity Server!"));
return;
}
callback.onResponse(object);
String cookies1 = response.getHeaders().get("Set-Cookie");
if (cookies1 ==null) {
errorCallback.onError(new NetworkError(100131, -1, "Error on logging in to Identity Server!"));
return;
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
errorCallback.onError(new NetworkError(100130, error.networkResponse.statusCode, "Error on logging in to Identity Server!"));
HashMap<String, String> object;
try {
object = getCookie(cookies1, new String[]{"_idp_session"});
} catch (NoSuchFieldException e) {
errorCallback.onError(new NetworkError(100132, -1, "Error on logging in to Identity Server!"));
return;
}
});
callback.onResponse(object);
}, error -> errorCallback.onError(new NetworkError(100130, error.networkResponse.statusCode, "Error on logging in to Identity Server!")));
}
/*
@@ -205,35 +168,27 @@ class KVVLogin extends HTTPService {
cookies.put("_idp_authn_lc_key", _idp_authn_lc_key);
cookies.put("ROUTEID", ROUTEID);
cookies.put("_idp_session", _idp_session);
get("https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO", cookies, new Response.Listener<Result>() {
@Override
public void onResponse(Result response) {
HashMap<String, String> object = new HashMap<>();
get("https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO", cookies, response -> {
HashMap<String, String> object = new HashMap<>();
Pattern pattern = Pattern.compile("ss&#x3a;mem&#x3a;([0-9a-f]+)");
Matcher matcher = pattern.matcher(response.getParsed());
if (!matcher.find()) {
errorCallback.onError(new NetworkError(100142, -1, "Error on getting SAML response!"));
return;
}
object.put("RelayState", "ss:mem:"+matcher.group(1));
pattern = Pattern.compile("([0-9a-zA-Z+]+==)");
matcher = pattern.matcher(response.getParsed());
if (!matcher.find()) {
errorCallback.onError(new NetworkError(100141, -1, "Error on getting SAML response!"));
return;
}
object.put("SAMLResponse", matcher.group(1));
callback.onResponse(object);
Pattern pattern = Pattern.compile("ss&#x3a;mem&#x3a;([0-9a-f]+)");
Matcher matcher = pattern.matcher(response.getParsed());
if (!matcher.find()) {
errorCallback.onError(new NetworkError(100142, -1, "Error on getting SAML response!"));
return;
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
errorCallback.onError(new NetworkError(100140, error.networkResponse.statusCode, "Error on getting SAML response!"));
object.put("RelayState", "ss:mem:"+matcher.group(1));
pattern = Pattern.compile("([0-9a-zA-Z+]+==)");
matcher = pattern.matcher(response.getParsed());
if (!matcher.find()) {
errorCallback.onError(new NetworkError(100141, -1, "Error on getting SAML response!"));
return;
}
});
object.put("SAMLResponse", matcher.group(1));
callback.onResponse(object);
}, error -> errorCallback.onError(new NetworkError(100140, error.networkResponse.statusCode, "Error on getting SAML response!")));
}
@@ -251,33 +206,25 @@ class KVVLogin extends HTTPService {
HashMap<String, String> body = new HashMap<>();
body.put("RelayState", RelayState);
body.put("SAMLResponse", SAMLResponse);
post("https://kvv.imp.fu-berlin.de/Shibboleth.sso/SAML2/POST", cookies, body, new Response.Listener<Result>() {
@Override
public void onResponse(Result response) {
String cookies = response.getHeaders().get("Set-Cookie");
if (cookies==null) {
errorCallback.onError(new NetworkError(100151, -1, "Error on starting KVV session!"));
return;
}
HashMap<String, String> object = new HashMap<>();
Pattern pattern = Pattern.compile("(_shibsession_[0-9a-f]+)=([^;]+);");
Matcher matcher = pattern.matcher(cookies);
if (!matcher.find()) {
errorCallback.onError(new NetworkError(100152, -1, "Error on starting Ident session!"));
}
object.put("shibsessionKey", matcher.group(1));
object.put("shibsessionName", matcher.group(2));
callback.onResponse(object);
post("https://kvv.imp.fu-berlin.de/Shibboleth.sso/SAML2/POST", cookies, body, response -> {
String cookies1 = response.getHeaders().get("Set-Cookie");
if (cookies1 ==null) {
errorCallback.onError(new NetworkError(100151, -1, "Error on starting KVV session!"));
return;
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
errorCallback.onError(new NetworkError(100150, error.networkResponse.statusCode, "Error on starting Ident session!"));
HashMap<String, String> object = new HashMap<>();
Pattern pattern = Pattern.compile("(_shibsession_[0-9a-f]+)=([^;]+);");
Matcher matcher = pattern.matcher(cookies1);
if (!matcher.find()) {
errorCallback.onError(new NetworkError(100152, -1, "Error on starting Ident session!"));
}
});
object.put("shibsessionKey", matcher.group(1));
object.put("shibsessionName", matcher.group(2));
callback.onResponse(object);
}, error -> errorCallback.onError(new NetworkError(100150, error.networkResponse.statusCode, "Error on starting Ident session!")));
}
@@ -288,17 +235,7 @@ class KVVLogin extends HTTPService {
_b1912c5a03d733a80bd3fee772bf68d4
*/
private void finishKVVlogin(LoginToken loginToken, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
get("https://kvv.imp.fu-berlin.de/sakai-login-tool/container", loginToken.getCookies(), new Response.Listener<Result>() {
@Override
public void onResponse(Result response) {
callback.onResponse(new HashMap<String, String>());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
errorCallback.onError(new NetworkError(100160, error.networkResponse.statusCode, "Cannot finish login process!"));
}
});
get("https://kvv.imp.fu-berlin.de/sakai-login-tool/container", loginToken.getCookies(), response -> callback.onResponse(new HashMap<>()), error -> errorCallback.onError(new NetworkError(100160, error.networkResponse.statusCode, "Cannot finish login process!")));
}

View File

@@ -38,7 +38,7 @@ public class KVVModuleList extends HTTPService {
get("https://kvv.imp.fu-berlin.de/direct/site.json", token.getCookies(), response -> {
String body = response.getParsed();
if (body == null) {
errorCallback.onError(new NetworkError(101101, 403, "No module list retreived!"));
errorCallback.onError(new NetworkError(101101, 403, "No module list retrieved!"));
return;
}
Modules modules = new Modules();
@@ -78,11 +78,14 @@ public class KVVModuleList extends HTTPService {
}
public void getModule(int index, final NetworkCallback<Modules.Module> callback, final NetworkErrorCallback errorCallback) {
this.getModuleList(success -> {
callback.onResponse(success.get(index));
}, errorCallback);
this.getModuleList(success -> callback.onResponse(success.get(index)), errorCallback);
}
public void getAnnouncements(Modules.Module module, final NetworkCallback<Modules.Module> callback, final NetworkErrorCallback errorCallback) {
queueModuleDetails.add(module.getID(), () -> {
if (module.announcements != null) {
@@ -103,7 +106,7 @@ public class KVVModuleList extends HTTPService {
get(String.format("https://kvv.imp.fu-berlin.de/direct/announcement/site/%s.json?n=999999&d=999999999", ID), token.getCookies(), response -> {
String body = response.getParsed();
if (body == null) {
errorCallback.onError(new NetworkError(101201, 403, "No announcements retreived!"));
errorCallback.onError(new NetworkError(101201, 403, "No announcements retrieved!"));
return;
}
ArrayList<Announcement> announcements = new ArrayList<>();
@@ -129,4 +132,28 @@ public class KVVModuleList extends HTTPService {
callback.onResponse(announcements);
}, error -> errorCallback.onError(new NetworkError(101203, error.networkResponse.statusCode, "Cannot get announcements!")));
}
public void getAssignments(Modules.Module module, final NetworkCallback<Modules.Module> callback, final NetworkErrorCallback errorCallback) {
queueModuleDetails.add(module.getID(), () -> {
if (module.assignments != null) {
callback.onResponse(module);
queueModuleDetails.next(module.getID());
return;
}
getAssignmentsUpgrade(module.getID(), success -> {
module.assignments = success;
callback.onResponse(module);
queueModuleDetails.next(module.getID());
}, errorCallback);
});
}
private void getAssignmentsUpgrade(String ID, final NetworkCallback<ArrayList<Assignment>> callback, final NetworkErrorCallback errorCallback) {
callback.onResponse(new ArrayList<Assignment>());
}
}

View File

@@ -160,13 +160,13 @@ public class Modules /*extends EventEmitter<Triplet<Integer, Modules.UpgradeModu
int size = list.size();
do {
next++;
} while (next < size && !accept(list.get(next)));
} while (next < size && decline(list.get(next)));
if (next == size)
next = -1;
}
private boolean accept(Module ob){
return ob.semester.equals(latestSemester);
private boolean decline(Module ob){
return !ob.semester.equals(latestSemester);
}
};
}
@@ -188,6 +188,7 @@ public class Modules /*extends EventEmitter<Triplet<Integer, Modules.UpgradeModu
public final String description;
private final String ID;
public ArrayList<Announcement> announcements;
public ArrayList<Assignment> assignments;
/*private Module() {
this(null, null, null, null, null);

View File

@@ -6,8 +6,6 @@ import android.util.Log;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.VolleyLog;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
@@ -35,17 +33,7 @@ public class MensaPlan {
post.put("date", "2017-10-13");
final String requestBody = post.toString();
StringRequest request = new StringRequest(Request.Method.POST, "https://www.stw.berlin/xhr/speiseplan-wochentag.html", new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("MensaPlan DDDD", response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d("MensaPlan EEEE", error.getMessage());
}
}) {
StringRequest request = new StringRequest(Request.Method.POST, "https://www.stw.berlin/xhr/speiseplan-wochentag.html", response -> Log.d("MensaPlan DDDD", response), error -> Log.d("MensaPlan EEEE", error.getMessage())) {
@Override
public String getBodyContentType() {
return "application/json; charset=utf-8";

View File

@@ -1,12 +1,9 @@
package de.sebse.fuplanner.tools;
import android.support.annotation.NonNull;
import java.util.HashMap;
import java.util.LinkedList;
import de.sebse.fuplanner.tools.network.NetworkCallback;
import de.sebse.fuplanner.tools.network.NetworkError;
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
public class AsyncQueue {

View File

@@ -1,5 +1,6 @@
package de.sebse.fuplanner.tools;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.os.Build;
import android.text.format.DateFormat;
@@ -16,13 +17,14 @@ public class Conversion {
return getModifiedDate(Locale.getDefault(), modified);
}
@SuppressLint("SimpleDateFormat")
public static String getModifiedDate(Locale locale, long modified) {
SimpleDateFormat dateFormat = null;
SimpleDateFormat dateFormat;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
dateFormat = new SimpleDateFormat(getDateFormat(locale));
} else {
dateFormat = new SimpleDateFormat("MMM/dd/yyyy hh:mm:ss aa");
dateFormat = new SimpleDateFormat("MMM/dd/yyyy hh:mm:ss aa", locale);
}
return dateFormat.format(new Date(modified));

View File

@@ -1,7 +1,5 @@
package de.sebse.fuplanner.tools;
import android.support.annotation.NonNull;
import org.intellij.lang.annotations.Language;
import java.util.Iterator;
@@ -26,38 +24,33 @@ public class Regex {
public static Iterable<MatchResult> allMatches(@Language("Regexp") String regex, final CharSequence input) {
final Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
return new Iterable<MatchResult>() {
@NonNull
public Iterator<MatchResult> iterator() {
return new Iterator<MatchResult>() {
// Use a matcher internally.
final Matcher matcher = pattern.matcher(input);
// Keep a match around that supports any interleaving of hasNext/next calls.
MatchResult pending;
return () -> new Iterator<MatchResult>() {
// Use a matcher internally.
final Matcher matcher = pattern.matcher(input);
// Keep a match around that supports any interleaving of hasNext/next calls.
MatchResult pending;
public boolean hasNext() {
// Lazily fill pending, and avoid calling find() multiple times if the
// clients call hasNext() repeatedly before sampling via next().
if (pending == null && matcher.find()) {
pending = matcher.toMatchResult();
}
return pending != null;
}
public MatchResult next() {
// Fill pending if necessary (as when clients call next() without
// checking hasNext()), throw if not possible.
if (!hasNext()) { throw new NoSuchElementException(); }
// Consume pending so next call to hasNext() does a find().
MatchResult next = pending;
pending = null;
return next;
}
/** Required to satisfy the interface, but unsupported. */
public void remove() { throw new UnsupportedOperationException(); }
};
public boolean hasNext() {
// Lazily fill pending, and avoid calling find() multiple times if the
// clients call hasNext() repeatedly before sampling via next().
if (pending == null && matcher.find()) {
pending = matcher.toMatchResult();
}
return pending != null;
}
public MatchResult next() {
// Fill pending if necessary (as when clients call next() without
// checking hasNext()), throw if not possible.
if (!hasNext()) { throw new NoSuchElementException(); }
// Consume pending so next call to hasNext() does a find().
MatchResult next = pending;
pending = null;
return next;
}
/** Required to satisfy the interface, but unsupported. */
public void remove() { throw new UnsupportedOperationException(); }
};
}
}

View File

@@ -37,7 +37,8 @@ public class BetterHurlStack extends HurlStack {
private final UrlRewriter mUrlRewriter;
private final SSLSocketFactory mSslSocketFactory;
private boolean followRedirects;
@SuppressWarnings({"unused", "FieldCanBeLocal"})
private boolean followRedirects; // TODO auto-redirect
BetterHurlStack() {
this(true);
@@ -130,7 +131,7 @@ public class BetterHurlStack extends HurlStack {
/**
* Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}.
* @param connection
* @param connection A http connection
* @return an HttpEntity populated with data from <code>connection</code>.
*/
private static HttpEntity entityFromConnection(HttpURLConnection connection) {

View File

@@ -2,7 +2,6 @@ package de.sebse.fuplanner.tools.network;
import android.content.Context;
import android.support.annotation.Nullable;
import android.util.Log;
import com.android.volley.AuthFailureError;
import com.android.volley.NetworkResponse;

View File

@@ -17,7 +17,8 @@
android:layout_width="128dp"
android:layout_height="128dp"
android:layout_marginBottom="24dp"
android:layout_gravity="center_horizontal" />
android:layout_gravity="center_horizontal"
android:contentDescription="@string/cd_ic_launcher"/>
<!-- Email Label -->
<android.support.design.widget.TextInputLayout
@@ -29,7 +30,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textEmailAddress"
android:hint="Username" />
android:hint="@string/username" />
</android.support.design.widget.TextInputLayout>
<!-- Password Label -->
@@ -42,7 +43,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:hint="Password"/>
android:hint="@string/password"/>
</android.support.design.widget.TextInputLayout>
<android.support.v7.widget.AppCompatButton
@@ -52,7 +53,7 @@
android:layout_marginTop="24dp"
android:layout_marginBottom="24dp"
android:padding="12dp"
android:text="Login"/>
android:text="@string/log_in"/>
</LinearLayout>

View File

@@ -9,6 +9,6 @@
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Announcement fragment" />
android:text="@string/lorem_ipsum_100" />
</FrameLayout>

View File

@@ -10,6 +10,7 @@
android:layout_width="@android:dimen/thumbnail_width"
android:layout_height="@android:dimen/thumbnail_height"
android:layout_gravity="center"
android:src="@mipmap/ic_launcher" />
android:src="@mipmap/ic_launcher"
android:contentDescription="@string/cd_ic_launcher"/>
</FrameLayout>

View File

@@ -1,14 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/caption"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:text="Caption"
tools:text="Caption"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Headline" />
</LinearLayout>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/caption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:text="@string/show_all"
android:textStyle="bold"
android:typeface="sans"
android:gravity="center"
android:textColor="@color/colorFUSecondary"
/>
</LinearLayout>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dip" >
@@ -15,21 +15,21 @@
tools:text="Test this new stuff!" />
<TextView
android:id="@+id/publisher"
android:id="@+id/sub_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/title"
android:layout_marginTop="5dip"
android:textColor="#343434"
android:textSize="12sp"
tools:text="Peter Müller" />
tools:text="Peter Bauer" />
<TextView
android:id="@+id/date"
android:id="@+id/sub_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/publisher"
android:layout_alignBottom="@+id/publisher"
android:layout_alignBaseline="@id/sub_left"
android:layout_alignBottom="@id/sub_left"
android:textColor="#343434"
android:textSize="12sp"
android:layout_alignParentEnd="true"

View File

@@ -1,14 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
<!-- sample xml -->
<com.ms.square.android.expandabletextview.ExpandableTextView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:expandableTextView="http://schemas.android.com/apk/res-auto"
android:id="@+id/expand_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
expandableTextView:maxCollapsedLines="4"
expandableTextView:animDuration="200">
<TextView
android:id="@id/expandable_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:textSize="16sp"
android:textColor="#666666" />
<ImageButton
android:id="@id/expand_collapse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:layout_gravity="end|bottom"
android:background="@android:color/transparent"
android:contentDescription="@string/description" />
</com.ms.square.android.expandabletextview.ExpandableTextView>
<!--<TextView
android:id="@+id/description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/text_margin"
android:text="@string/lorem_ipsum_100"
android:textAppearance="?attr/textAppearanceListItem" />
android:textAppearance="?attr/textAppearanceListItem" />-->
</LinearLayout>

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="@dimen/nav_header_height"
android:background="@drawable/side_nav_bar"
@@ -17,7 +18,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/Widget.AppCompat.Button.Colored"
android:text="Login"
android:text="@string/log_in"
android:visibility="gone" />
@@ -27,14 +28,15 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@mipmap/ic_launcher"
android:visibility="gone" />
android:visibility="gone"
android:contentDescription="@string/cd_ic_launcher"/>
<TextView
android:id="@+id/login_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:text="Android Studio"
tools:text="Android Studio"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:visibility="gone" />
</LinearLayout>

View File

@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="@dimen/nav_header_height"
android:background="@drawable/side_nav_bar"
android:gravity="bottom"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:theme="@style/ThemeOverlay.AppCompat.Dark">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@mipmap/ic_launcher" />
<TextView
android:id="@+id/login_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:text="Android Studio"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
</LinearLayout>

View File

@@ -5,15 +5,15 @@
<item
android:id="@+id/nav_dining"
android:icon="@drawable/ic_local_dining"
android:title="Mensaplan" />
android:title="@string/canteen_plan" />
</group>
<item android:title="Optionen">
<item android:title="@string/options">
<menu>
<item
android:id="@+id/nav_share"
android:icon="@drawable/ic_menu_share"
android:title="Teilen"/>
android:title="@string/share"/>
</menu>
</item>

View File

@@ -5,37 +5,37 @@
<item
android:id="@+id/nav_schedule"
android:icon="@drawable/ic_event"
android:title="Stundenplan"
android:title="@string/schedule"
android:orderInCategory="10"/>
<item
android:id="@+id/nav_modules"
android:icon="@drawable/ic_apps"
android:title="Module"
android:title="@string/events"
android:orderInCategory="100" />
<item
android:id="@+id/nav_dining"
android:icon="@drawable/ic_local_dining"
android:title="Mensaplan"
android:title="@string/canteen_plan"
android:orderInCategory="200"/>
<item
android:id="@+id/nav_settings"
android:icon="@drawable/ic_settings"
android:title="Einstellungen"
android:title="@string/settings"
android:orderInCategory="300"/>
</group>
<item android:title="Optionen"
<item android:title="@string/options"
android:orderInCategory="499">
<menu>
<item
android:id="@+id/nav_logout"
android:icon="@drawable/ic_exit_to_app"
android:title="Logout"
android:title="@string/log_out"
android:orderInCategory="500"/>
<item
android:id="@+id/nav_share"
android:icon="@drawable/ic_menu_share"
android:title="Teilen"
android:title="@string/share"
android:orderInCategory="600"/>
</menu>
</item>

View File

@@ -7,10 +7,21 @@
<string name="action_settings">Settings</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
<string name="overview">Overview</string>
<string name="announcements">Announcements</string>
<string name="lorem_ipsum_100">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.</string>
<string name="description">Description</string>
<string name="announcements_count">Announcements (%1$d)</string>
<string name="cd_ic_launcher">App Icon</string>
<string name="schedule">Schedule</string>
<string name="events">Events</string>
<string name="canteen_plan">Canteen Plan</string>
<string name="settings">Settings</string>
<string name="options">Options</string>
<string name="log_out">Log out</string>
<string name="share">Share</string>
<string name="log_in">Log in</string>
<string name="username">Username</string>
<string name="password">Password</string>
<string name="show_all">Show All</string>
</resources>