Working Progress (not runnable)

This commit is contained in:
Caesar2011
2018-11-07 03:30:38 +01:00
parent 44b95d7b70
commit 3cdce73c5a
29 changed files with 1989 additions and 138 deletions

View File

@@ -24,15 +24,6 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.my.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application> </application>
</manifest> </manifest>

View File

@@ -33,16 +33,20 @@ import de.sebse.fuplanner.fragments.canteen.DaySwitcherFragment;
import de.sebse.fuplanner.fragments.moddetails.ModDetailFragment; import de.sebse.fuplanner.fragments.moddetails.ModDetailFragment;
import de.sebse.fuplanner.services.Canteen.CanteenBrowser; import de.sebse.fuplanner.services.Canteen.CanteenBrowser;
import de.sebse.fuplanner.services.Canteen.types.Canteen; import de.sebse.fuplanner.services.Canteen.types.Canteen;
import de.sebse.fuplanner.services.GoogleAuth.Credentials;
import de.sebse.fuplanner.services.GoogleAuth.GoogleAuth; import de.sebse.fuplanner.services.GoogleAuth.GoogleAuth;
import de.sebse.fuplanner.services.KVV.KVV; import de.sebse.fuplanner.services.NewKVV.KVV;
import de.sebse.fuplanner.services.KVV.types.LoginToken; import de.sebse.fuplanner.services.KVV.types.LoginToken;
import de.sebse.fuplanner.services.KVV.types.Modules; import de.sebse.fuplanner.services.NewKVV.KVVListener;
import de.sebse.fuplanner.tools.MainActivityListener; import de.sebse.fuplanner.tools.MainActivityListener;
import de.sebse.fuplanner.tools.RequestPermissionsResultListener; import de.sebse.fuplanner.tools.RequestPermissionsResultListener;
import de.sebse.fuplanner.tools.logging.Logger; 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 public class MainActivity extends AppCompatActivity
implements MainActivityListener, implements MainActivityListener, KVVListener,
NavigationView.OnNavigationItemSelectedListener, NavigationView.OnNavigationItemSelectedListener,
LoginFragment.OnLoginFragmentInteractionListener, LoginFragment.OnLoginFragmentInteractionListener,
ModulesFragment.OnModulesFragmentInteractionListener, ModulesFragment.OnModulesFragmentInteractionListener,
@@ -63,26 +67,25 @@ public class MainActivity extends AppCompatActivity
private FragmentManager mFragmentManager; private FragmentManager mFragmentManager;
private GoogleAuth mGoogleAuth; private GoogleAuth mGoogleAuth;
private KVV mKVV; private de.sebse.fuplanner.services.KVV.KVV mKVV;
private KVV mNewKVV;
private final Logger log = new Logger(this); private final Logger log = new Logger(this);
private NavigationView mNavigationView; private NavigationView mNavigationView;
private int fragmentPage = FRAGMENT_NONE; private int fragmentPage = FRAGMENT_NONE;
private int currentPage = FRAGMENT_NONE;
private String fragmentData = ""; private String fragmentData = "";
private String currentData = "";
private CanteenBrowser mCanteenBrowser; private CanteenBrowser mCanteenBrowser;
private boolean mOfflineMode = false;
private HashMap<String, RequestPermissionsResultListener> permissionListeners = new HashMap<>(); private HashMap<String, RequestPermissionsResultListener> permissionListeners = new HashMap<>();
private boolean mOfflineBanner;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
int newFragmentPage = FRAGMENT_NONE; int desiredPage = getDefaultFragmentAfterLogin();
String newFragmentData = ""; String desiredData = "";
if (savedInstanceState != null) { if (savedInstanceState != null) {
newFragmentPage = savedInstanceState.getInt(ARG_FRAGMENT_PAGE, fragmentPage); desiredPage = savedInstanceState.getInt(ARG_FRAGMENT_PAGE, desiredPage);
newFragmentData = savedInstanceState.getString(ARG_FRAGMENT_STATUS, fragmentData); desiredData = savedInstanceState.getString(ARG_FRAGMENT_STATUS, desiredData);
} }
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
@@ -99,15 +102,9 @@ public class MainActivity extends AppCompatActivity
mNavigationView.setNavigationItemSelectedListener(this); mNavigationView.setNavigationItemSelectedListener(this);
mFragmentManager = getSupportFragmentManager(); mFragmentManager = getSupportFragmentManager();
LoginToken loginToken = getKVV().easyLogin(); getNewKVV().account().doOfflineLogin();
if (loginToken == null) { updateNavigation();
checkAndDoLogin(); changeFragment(desiredPage, desiredData);
} else {
if (newFragmentPage != FRAGMENT_LOGIN && newFragmentPage != FRAGMENT_STARTUP && newFragmentPage != FRAGMENT_NONE)
toLoginState(loginToken, newFragmentPage, newFragmentData);
else
toLoginState(loginToken, getDefaultFragmentAfterLogin(), "");
}
} }
@Override @Override
@@ -123,7 +120,7 @@ public class MainActivity extends AppCompatActivity
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present. // Inflate the menu; this adds items to the action bar if it is present.
if (currentPage == FRAGMENT_SCHEDULE) { if (fragmentPage == FRAGMENT_SCHEDULE) {
getMenuInflater().inflate(R.menu.options_schedule, menu); getMenuInflater().inflate(R.menu.options_schedule, menu);
return true; return true;
} }
@@ -180,13 +177,13 @@ public class MainActivity extends AppCompatActivity
startActivity(sendIntent); startActivity(sendIntent);
break; break;
case R.id.nav_logout: case R.id.nav_logout:
this.getKVV().logout(); getNewKVV().account().logout(true);
this.getGoogleAuth().getLoginState(credentials -> { this.getGoogleAuth().getLoginState(credentials -> {
if (credentials != null) { if (credentials != null) {
this.getGoogleAuth().deleteLoginState(credentials.getUsername(), credentials.getPassword()); this.getGoogleAuth().deleteLoginState(credentials.getUsername(), credentials.getPassword());
} }
this.toLogoutState();
}); });
this.toLogoutState();
break; break;
} }
@@ -210,13 +207,14 @@ public class MainActivity extends AppCompatActivity
@Override @Override
protected void onSaveInstanceState(Bundle savedInstanceState) { protected void onSaveInstanceState(Bundle savedInstanceState) {
Fragment fragment = mFragmentManager.findFragmentByTag(String.valueOf(fragmentPage)); if (fragmentPage != FRAGMENT_STARTUP && fragmentPage != FRAGMENT_NONE && fragmentPage != FRAGMENT_LOGIN) {
savedInstanceState.putInt(ARG_FRAGMENT_PAGE, fragmentPage); Fragment fragment = mFragmentManager.findFragmentByTag(String.valueOf(fragmentPage));
savedInstanceState.putString(ARG_FRAGMENT_STATUS, fragmentData); savedInstanceState.putInt(ARG_FRAGMENT_PAGE, fragmentPage);
if (fragment instanceof ModDetailFragment) { if (fragment instanceof ModDetailFragment) {
savedInstanceState.putString(ARG_FRAGMENT_STATUS, ((ModDetailFragment) fragment).getData()); savedInstanceState.putString(ARG_FRAGMENT_STATUS, ((ModDetailFragment) fragment).getData());
} else { } else {
savedInstanceState.putString(ARG_FRAGMENT_STATUS, fragmentData); savedInstanceState.putString(ARG_FRAGMENT_STATUS, fragmentData);
}
} }
super.onSaveInstanceState(savedInstanceState); super.onSaveInstanceState(savedInstanceState);
} }
@@ -239,13 +237,21 @@ public class MainActivity extends AppCompatActivity
return this.mGoogleAuth; return this.mGoogleAuth;
} }
public KVV getKVV() { @Deprecated
public de.sebse.fuplanner.services.KVV.KVV getKVV() {
if (this.mKVV == null) { if (this.mKVV == null) {
this.mKVV = new KVV(this); this.mKVV = new de.sebse.fuplanner.services.KVV.KVV(this);
} }
return this.mKVV; return this.mKVV;
} }
public KVV getNewKVV() {
if (this.mNewKVV == null) {
this.mNewKVV = new KVV(this, this);
}
return this.mNewKVV;
}
public CanteenBrowser getCanteenBrowser() { public CanteenBrowser getCanteenBrowser() {
if (this.mCanteenBrowser == null) { if (this.mCanteenBrowser == null) {
this.mCanteenBrowser = new CanteenBrowser(this); this.mCanteenBrowser = new CanteenBrowser(this);
@@ -254,23 +260,13 @@ public class MainActivity extends AppCompatActivity
} }
private int getDefaultFragmentAfterLogin() { private int getDefaultFragmentAfterLogin() {
return getDefaultFragmentAfterLogin(new String[1]); return FRAGMENT_MODULES;
}
private int getDefaultFragmentAfterLogin(String[] id) {
if (fragmentPage == FRAGMENT_NONE){
id[0] = "";
return FRAGMENT_MODULES;
}
else {
id[0] = fragmentData;
return fragmentPage;
}
} }
private void toLogoutState() { private void toLogoutState() {
setOfflineBanner(true); setOfflineBanner(false);
setRefreshFailedBanner(false); setRefreshFailedBanner(false);
updateNavigation();
changeFragment(FRAGMENT_LOGIN); changeFragment(FRAGMENT_LOGIN);
} }
@@ -283,14 +279,17 @@ public class MainActivity extends AppCompatActivity
} }
private void toLoginState(String fullName, String email, int newFragment, String newData, boolean onlineMode) { private void toLoginState(String fullName, String email, int newFragment, String newData, boolean onlineMode) {
setOfflineBanner(onlineMode); setOfflineBanner(!onlineMode);
changeFragment(newFragment, newData); updateNavigation();
View header = mNavigationView.getHeaderView(0); View header = mNavigationView.getHeaderView(0);
((TextView) header.findViewById(R.id.login_name)).setText(fullName); ((TextView) header.findViewById(R.id.login_name)).setText(fullName);
((TextView) header.findViewById(R.id.login_mail)).setText(email); ((TextView) header.findViewById(R.id.login_mail)).setText(email);
changeFragment(newFragment, newData);
} }
@Deprecated
private void checkAndDoLogin() { private void checkAndDoLogin() {
changeFragment(FRAGMENT_STARTUP); changeFragment(FRAGMENT_STARTUP);
getGoogleAuth().getLoginState(credentials -> { getGoogleAuth().getLoginState(credentials -> {
@@ -298,10 +297,9 @@ public class MainActivity extends AppCompatActivity
toLogoutState(); toLogoutState();
return; return;
} }
String[] id = {""};
int fragment = getDefaultFragmentAfterLogin(id); int fragment = getDefaultFragmentAfterLogin();
this.getKVV().login(credentials.getUsername(), credentials.getPassword(), success -> toLoginState(success, fragment , id[0]), this.getKVV().login(credentials.getUsername(), credentials.getPassword(), success -> toLoginState(success, fragment, ""),
error -> { error -> {
log.e(error); log.e(error);
toLogoutState(); toLogoutState();
@@ -355,56 +353,38 @@ public class MainActivity extends AppCompatActivity
fragmentTransaction.commit(); fragmentTransaction.commit();
if (newFragment == FRAGMENT_STARTUP) { if (newFragment == FRAGMENT_STARTUP) {
findViewById(R.id.app_bar_layout).setVisibility(View.GONE); findViewById(R.id.app_bar_include).setVisibility(View.GONE);
} else { } else {
findViewById(R.id.app_bar_layout).setVisibility(View.VISIBLE); findViewById(R.id.app_bar_include).setVisibility(View.VISIBLE);
} }
boolean isChangeLoginState = this.fragmentPage = newFragment;
( this.fragmentData = newData;
(newFragment == FRAGMENT_STARTUP || newFragment == FRAGMENT_LOGIN) &&
(currentPage != FRAGMENT_STARTUP && currentPage != FRAGMENT_LOGIN)
) || (
(currentPage == FRAGMENT_STARTUP || currentPage == FRAGMENT_LOGIN || currentPage == FRAGMENT_NONE) &&
(newFragment != FRAGMENT_STARTUP && newFragment != FRAGMENT_LOGIN && (getKVV().isLoggedIn() || mOfflineMode))
);
if (newFragment != FRAGMENT_STARTUP && newFragment != FRAGMENT_NONE && newFragment != FRAGMENT_LOGIN) {
this.fragmentPage = newFragment;
this.fragmentData = newData;
}
this.currentPage = newFragment;
this.currentData = newData;
invalidateOptionsMenu(); invalidateOptionsMenu();
if (isChangeLoginState) //TODO navigation selection
refreshNavigation();
else
setNavigationSelection(currentPage, currentData);
} }
private void setOfflineBanner(boolean onlineMode) { private void setOfflineBanner(boolean visible) {
View offline_header = findViewById(R.id.offline_msg); View offline_header = findViewById(R.id.offline_msg);
if (onlineMode) offline_header.setVisibility(visible ? View.VISIBLE : View.GONE);
offline_header.setVisibility(View.GONE); mOfflineBanner = visible;
else
offline_header.setVisibility(View.VISIBLE);
mOfflineMode = !onlineMode;
} }
private void setRefreshFailedBanner(boolean refreshFailed) { private void setRefreshFailedBanner(boolean refreshFailed) {
View viewNoConnection = findViewById(R.id.no_connection_msg); View viewNoConnection = findViewById(R.id.no_connection_msg);
if (!mOfflineMode && refreshFailed) if (!mOfflineBanner && refreshFailed)
viewNoConnection.setVisibility(View.VISIBLE); viewNoConnection.setVisibility(View.VISIBLE);
else else
viewNoConnection.setVisibility(View.GONE); viewNoConnection.setVisibility(View.GONE);
} }
private void setNavigationSelection(int fragment, String data) { private void setNavigationSelection() {
MenuItem item; MenuItem item;
switch (fragment) { switch (fragmentPage) {
case FRAGMENT_MODULES_DETAILS: case FRAGMENT_MODULES_DETAILS:
getKVV().getModule(data, success -> { getKVV().getModule(fragmentData, success -> {
int size = mNavigationView.getMenu().size(); int size = mNavigationView.getMenu().size();
//noinspection ConstantConditions //noinspection ConstantConditions
String title = success == null ? null : success.title; String title = success == null ? null : success.title;
@@ -426,7 +406,7 @@ public class MainActivity extends AppCompatActivity
case FRAGMENT_CANTEENS_DETAILS: case FRAGMENT_CANTEENS_DETAILS:
getCanteenBrowser().getCanteens(success -> { getCanteenBrowser().getCanteens(success -> {
int size = mNavigationView.getMenu().size(); int size = mNavigationView.getMenu().size();
Canteen canteen = success.getCanteen(Integer.parseInt(data)); Canteen canteen = success.getCanteen(Integer.parseInt(fragmentData));
//noinspection ConstantConditions //noinspection ConstantConditions
String title = canteen == null ? null : canteen.getName(); String title = canteen == null ? null : canteen.getName();
for (int k = 0; k < size; k++) { for (int k = 0; k < size; k++) {
@@ -466,10 +446,10 @@ public class MainActivity extends AppCompatActivity
private void afterAnyMenuInflate(boolean isLoggedIn) { private void afterAnyMenuInflate(boolean isLoggedIn) {
if (isLoggedIn) { if (isLoggedIn) {
getKVV().getModuleList(success -> { getNewKVV().modules().list().recv(success -> {
int i = 0; int i = 0;
for (Iterator<Modules.Module> it = success.latestSemesterIterator(); it.hasNext(); ) { for (Iterator<de.sebse.fuplanner.services.NewKVV.types.Modules.Module> it = success.latestSemesterIterator(); it.hasNext(); ) {
Modules.Module module = it.next(); de.sebse.fuplanner.services.NewKVV.types.Modules.Module module = it.next();
MenuItem menuItem = mNavigationView.getMenu().add(Menu.NONE, Menu.NONE, 101 + i, module.title); MenuItem menuItem = mNavigationView.getMenu().add(Menu.NONE, Menu.NONE, 101 + i, module.title);
menuItem.setOnMenuItemClickListener(item -> { menuItem.setOnMenuItemClickListener(item -> {
onModulesFragmentInteraction(module.getID()); onModulesFragmentInteraction(module.getID());
@@ -492,16 +472,30 @@ public class MainActivity extends AppCompatActivity
}, log::e); }, log::e);
} }
private void updateNavigation() {
boolean isLoggedIn = getNewKVV().account().isLoggedIn();
setNavigationHeader(isLoggedIn);
mNavigationView.getMenu().clear();
if (isLoggedIn)
mNavigationView.inflateMenu(R.menu.activity_main_drawer_login);
else
mNavigationView.inflateMenu(R.menu.activity_main_drawer);
afterAnyMenuInflate(isLoggedIn);
setNavigationSelection();
}
@Override
public void onLoginFragmentInteraction(LoginToken loginToken, boolean onlineMode) { public void onLoginFragmentInteraction(LoginToken loginToken, boolean onlineMode) {
String[] id = {""}; int fragment = getDefaultFragmentAfterLogin();
int fragment = getDefaultFragmentAfterLogin(id); toLoginState(loginToken.getFullName(), loginToken.getEmail(), fragment, "", onlineMode);
toLoginState(loginToken.getFullName(), loginToken.getEmail(), fragment, id[0], onlineMode);
} }
@Override @Override
@@ -524,6 +518,7 @@ public class MainActivity extends AppCompatActivity
setTitle(titleId); setTitle(titleId);
} }
@Deprecated
@Override @Override
public void loginTokenInvalid(boolean doLoginCheck) { public void loginTokenInvalid(boolean doLoginCheck) {
if (doLoginCheck) { if (doLoginCheck) {
@@ -564,16 +559,33 @@ public class MainActivity extends AppCompatActivity
Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
} }
@Override @Override
public void refreshNavigation() { public void getCredentials(NetworkCallback<Credentials> callback, NetworkErrorCallback error) {
boolean isLoggedIn = getKVV().isLoggedIn() || mOfflineMode; getGoogleAuth().getLoginState(credentials -> {
setNavigationHeader(isLoggedIn); if (credentials == null || credentials.getUsername() == null || credentials.getPassword() == null) {
mNavigationView.getMenu().clear(); error.onError(new NetworkError(200100, 403, "No Google Login available!"));
if (isLoggedIn) } else {
mNavigationView.inflateMenu(R.menu.activity_main_drawer_login); callback.onResponse(credentials);
else }
mNavigationView.inflateMenu(R.menu.activity_main_drawer); });
afterAnyMenuInflate(isLoggedIn); }
setNavigationSelection(currentPage, currentData);
@Override
public void handleLogin(de.sebse.fuplanner.services.NewKVV.types.LoginToken token, boolean enteringOnlineMode) {
toLoginState(token.getUsername(), token.getEmail(), getDefaultFragmentAfterLogin(), "", enteringOnlineMode);
}
@Override
public void handleLogout() {
toLogoutState();
} }
} }

View File

@@ -117,9 +117,9 @@ public class ModDetailAnnounceFragment extends Fragment implements Download.OnDo
super.onAttach(context); super.onAttach(context);
if (context instanceof MainActivityListener) { if (context instanceof MainActivityListener) {
this.context = ((MainActivityListener) context); this.context = ((MainActivityListener) context);
this.context.addRequestPermissionsResultListener(getDownload().getRequestPermissionsResultListener(), "ModDetailResourceFragment"); this.context.addRequestPermissionsResultListener(getDownload().getRequestPermissionsResultListener(), "ModDetailAnnounceFragment");
} else } else
throw new RuntimeException(context.toString() + " must implement MainActivityListener"); throw new RuntimeException(context.toString() + " must implement ModDetailAnnounceFragment");
} }
@Override @Override

View File

@@ -117,7 +117,7 @@ public class ModDetailAssignmentFragment extends Fragment implements Download.On
super.onAttach(context); super.onAttach(context);
if (context instanceof MainActivityListener) { if (context instanceof MainActivityListener) {
this.context = ((MainActivityListener) context); this.context = ((MainActivityListener) context);
this.context.addRequestPermissionsResultListener(getDownload().getRequestPermissionsResultListener(), "ModDetailResourceFragment"); this.context.addRequestPermissionsResultListener(getDownload().getRequestPermissionsResultListener(), "ModDetailAssignmentFragment");
} else } else
throw new RuntimeException(context.toString() + " must implement MainActivityListener"); throw new RuntimeException(context.toString() + " must implement MainActivityListener");
} }
@@ -125,7 +125,7 @@ public class ModDetailAssignmentFragment extends Fragment implements Download.On
@Override @Override
public void onDetach() { public void onDetach() {
super.onDetach(); super.onDetach();
this.context.removeRequestPermissionsResultListener("ModDetailResourceFragment"); this.context.removeRequestPermissionsResultListener("ModDetailAssignmentFragment");
} }
Download getDownload() { Download getDownload() {

View File

@@ -29,6 +29,7 @@ public class GoogleAuth {
private static final String TAG = "GoogleAuth"; private static final String TAG = "GoogleAuth";
private final FragmentActivity activity; private final FragmentActivity activity;
private static final String FU_PLANNER_PROVIDER = "FUPlanner";
private CredentialsClient mCredentialsClient; private CredentialsClient mCredentialsClient;
private boolean mIsResolving; private boolean mIsResolving;
@Nullable @Nullable
@@ -61,6 +62,7 @@ public class GoogleAuth {
connect(); connect();
CredentialRequest request = new CredentialRequest.Builder() CredentialRequest request = new CredentialRequest.Builder()
.setPasswordLoginSupported(true) .setPasswordLoginSupported(true)
.setAccountTypes(FU_PLANNER_PROVIDER)
.build(); .build();

View File

@@ -15,7 +15,6 @@ import java.util.Arrays;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.content.FileProvider;
import de.sebse.fuplanner.MainActivity; import de.sebse.fuplanner.MainActivity;
import de.sebse.fuplanner.R; import de.sebse.fuplanner.R;
import de.sebse.fuplanner.services.KVV.types.Resource; import de.sebse.fuplanner.services.KVV.types.Resource;
@@ -24,10 +23,7 @@ import de.sebse.fuplanner.tools.RequestPermissionsResultListener;
import de.sebse.fuplanner.tools.UtilsDate; import de.sebse.fuplanner.tools.UtilsDate;
import de.sebse.fuplanner.tools.logging.Logger; import de.sebse.fuplanner.tools.logging.Logger;
import static android.content.Intent.normalizeMimeType;
import static androidx.core.app.ActivityCompat.startActivityForResult;
import static androidx.core.content.ContextCompat.checkSelfPermission; import static androidx.core.content.ContextCompat.checkSelfPermission;
import static androidx.core.content.ContextCompat.startActivity;
public class Download { public class Download {
@@ -35,7 +31,6 @@ public class Download {
private final ActivityInterface activityInterface; private final ActivityInterface activityInterface;
private RequestedDownload requestedDownload; private RequestedDownload requestedDownload;
private Logger log = new Logger(this); private Logger log = new Logger(this);
static final int REQUEST_IMAGE_OPEN = 1;
public Download(ContextInterface contextInterface, ActivityInterface activityInterface) { public Download(ContextInterface contextInterface, ActivityInterface activityInterface) {
@@ -60,7 +55,7 @@ public class Download {
if (file.getModifiedDate() != 0) { if (file.getModifiedDate() != 0) {
if (!message.isEmpty()) if (!message.isEmpty())
message += "\n"; message += "\n";
message += resources.getString(R.string.last_modified_on, UtilsDate.getModifiedDateTime(contextInterface.get(), file.getModifiedDate())); resources.getString(R.string.last_modified_on, UtilsDate.getModifiedDateTime(contextInterface.get(), file.getModifiedDate()));
} }
alertDialogBuilder alertDialogBuilder
@@ -186,17 +181,9 @@ public class Download {
} }
private void fileOpen(File url){ private void fileOpen(File url){
Uri uri = Uri.fromFile(url);
Uri uri = FileProvider.getUriForFile(contextInterface.get(), contextInterface.get().getApplicationContext().getPackageName() + ".my.provider", url); Intent intent = new Intent();//Intent.ACTION_VIEW
Intent intent;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
intent = new Intent(Intent.ACTION_VIEW);
} else {
intent = new Intent();
}
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check what kind of file you are trying to open, by comparing the url with extensions. // Check what kind of file you are trying to open, by comparing the url with extensions.
// When the if condition is matched, plugin sets the correct intent (mime) type, // When the if condition is matched, plugin sets the correct intent (mime) type,
// so Android knew what application to use to open the file // so Android knew what application to use to open the file
@@ -235,18 +222,13 @@ public class Download {
intent.setDataAndType(uri, "video/*"); intent.setDataAndType(uri, "video/*");
} else { } else {
//if you want you can also define the intent type for any other file //if you want you can also define the intent type for any other file
//additionally use else clause below, to manage other unknown extensions //additionally use else clause below, to manage other unknown extensions
//in this case, Android will show all applications installed on the device //in this case, Android will show all applications installed on the device
//so you can choose which application to use //so you can choose which application to use
intent.setDataAndType(uri, "*/*"); intent.setDataAndType(uri, "*/*");
} }
//intent.addCategory(Intent.CATEGORY_OPENABLE);
// Only the system receives the ACTION_OPEN_DOCUMENT, so no need to test.
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
contextInterface.get().startActivity(intent); contextInterface.get().startActivity(intent);

View File

@@ -0,0 +1,47 @@
package de.sebse.fuplanner.services.NewKVV;
import android.content.Context;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
public class KVV {
private final HashMap<String, Object> addons = new HashMap<>();
private final KVVListener mListener;
private final Context mContext;
public KVV(KVVListener listener, Context context) {
this.mListener = listener;
this.mContext = context;
}
@NotNull
public KVVLogin account() {
return (KVVLogin) addAndGet("account", () -> new KVVLogin(mListener, mContext));
}
@NotNull
public KVVModules modules() {
return (KVVModules) addAndGet("module", () -> new KVVModules(account(), mContext));
}
@NotNull
private Object addAndGet(@NotNull String addon, @NotNull ModuleCreatorInterface creatorInterface) {
Object o = addons.get(addon);
if (o == null) {
o = creatorInterface.create();
addons.put(addon, o);
}
return o;
}
private interface ModuleCreatorInterface {
@NotNull Object create();
}
}

View File

@@ -0,0 +1,14 @@
package de.sebse.fuplanner.services.NewKVV;
import de.sebse.fuplanner.services.GoogleAuth.Credentials;
import de.sebse.fuplanner.services.NewKVV.types.LoginToken;
import de.sebse.fuplanner.tools.network.NetworkCallback;
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
public interface KVVListener {
void getCredentials(NetworkCallback<Credentials> callback, NetworkErrorCallback error);
void handleLogin(LoginToken token, boolean enteringOnlineMode);
void handleLogout();
}

View File

@@ -0,0 +1,433 @@
package de.sebse.fuplanner.services.NewKVV;
import android.content.Context;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import androidx.annotation.Nullable;
import de.sebse.fuplanner.services.NewKVV.types.LoginToken;
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;
public class KVVLogin extends HTTPService {
private KVVListener mListener;
@Nullable private LoginToken mToken;
private boolean mLoginPending = false;
private boolean mOnlineMode = false;
KVVLogin(KVVListener listener, Context context) {
super(context);
this.mListener = listener;
}
public void doOnlineLogin(String username, String password, NetworkCallback<LoginToken> callback, NetworkErrorCallback errorCallback) {
if (mLoginPending) {
errorCallback.onError(new NetworkError(100160, -1, "Login already pending!"));
}
if (mToken != null) {
errorCallback.onError(new NetworkError(100161, -1, "Already logged in!"));
}
mLoginPending = true;
doLogin(username, password, token -> {
testLoginToken(token2 -> {
setToken(token, true);
mLoginPending = false;
callback.onResponse(token);
}, error -> {
mLoginPending = false;
errorCallback.onError(error);
});
}, error -> {
mLoginPending = false;
errorCallback.onError(error);
});
}
public boolean doOfflineLogin() {
if (mLoginPending || mToken != null)
return false;
mLoginPending = true;
boolean result = false;
try {
result = setToken(LoginToken.load(getContext()), false);
} catch (FileNotFoundException ignored) {
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
mLoginPending = false;
return result;
}
public boolean isOfflineStoredAvailable() {
try {
LoginToken.load(getContext());
return true;
} catch (FileNotFoundException ignored) {
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return false;
}
public boolean logout(boolean delete) {
if (mLoginPending)
return false;
if (mToken == null)
return true;
if (delete)
mToken.delete(getContext());
mToken = null;
return handleCallbacks();
}
public boolean isLoginPending() {
return mLoginPending;
}
public boolean isLoggedIn() {
return mToken != null;
}
public boolean isInOfflineMode() {
return isLoggedIn() && !mOnlineMode;
}
public boolean isInOnlineMode() {
return isLoggedIn() && mOnlineMode;
}
public void testLoginToken(NetworkCallback<LoginToken> callback, NetworkErrorCallback errorCallback) {
if (mToken == null) {
errorCallback.onError(new NetworkError(100173, -1, "Not logged in!"));
return;
}
get(String.format("https://kvv.imp.fu-berlin.de/direct/profile/%s.json", mToken.getUsername()), mToken.getCookies(), response -> {
String body = response.getParsed();
if (body == null) {
errorCallback.onError(new NetworkError(100172, 403, "Testing login failed!"));
return;
}
try {
JSONObject json = new JSONObject(body);
String displayName = json.getString("displayName");
String email = json.getString("email");
mToken.setAdditionals(displayName, email);
callback.onResponse(mToken);
} catch (JSONException e) {
errorCallback.onError(new NetworkError(100171, 403, "Cannot parse profile!"));
}
}, error -> errorCallback.onError(new NetworkError(100170, error.networkResponse.statusCode, "Testing login failed!")));
}
@Nullable public LoginToken getLoginToken() {
return mToken;
}
void refreshLogin(NetworkCallback<LoginToken> success, NetworkErrorCallback error) {
mListener.getCredentials(credentials -> {
doOnlineLogin(credentials.getUsername(), credentials.getPassword(), success, error);
}, e -> {
logout(false);
error.onError(e);
});
}
private boolean handleCallbacks() {
if (mToken != null) {
mListener.handleLogin(mToken, false);
return true;
} else {
mListener.handleLogout();
return false;
}
}
private boolean setToken(@Nullable LoginToken token, boolean enteringOnlineMode) {
if (token == null)
return false;
mToken = token;
if (enteringOnlineMode) {
try {
mToken.save(getContext());
} catch (IOException e) {
e.printStackTrace();
}
}
mOnlineMode = !enteringOnlineMode;
return handleCallbacks();
}
private void doLogin(String username, String password, NetworkCallback<LoginToken> callback, NetworkErrorCallback error) {
startKVVSession(success -> {
String kvvJSESSIONID = success.get("JSESSIONID");
getSAMLRequest(kvvJSESSIONID, success1 -> startIdentSession(success1.get("Location"), success11 -> {
String identJSESSIONID = success11.get("JSESSIONID");
String ident_idp_authn_lc_key = success11.get("_idp_authn_lc_key");
String identROUTEID = success11.get("ROUTEID");
loginIdent(true, username, password, identJSESSIONID, ident_idp_authn_lc_key, identROUTEID, success111 -> loginIdent(false, username, password, identJSESSIONID, ident_idp_authn_lc_key, identROUTEID, success11112 -> {
String ident_idp_session = success11112.get("_idp_session");
getSAMLResponse(identJSESSIONID, ident_idp_authn_lc_key, identROUTEID, ident_idp_session, success1111 -> loginKVV(success1111.get("RelayState"), success1111.get("SAMLResponse"), kvvJSESSIONID, success111112 -> {
LoginToken token = new LoginToken(username, success111112.get("shibsessionKey"), success111112.get("shibsessionName"), kvvJSESSIONID);
finishKVVlogin(token, success11111 -> callback.onResponse(token), error);
}, error), error);
}, error), error);
}, error), error);
}, error);
}
/*
GET https://kvv.imp.fu-berlin.de/portal/login
-> 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, 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);
}, error -> errorCallback.onError(new NetworkError(100100, error.networkResponse.statusCode, "Error on starting KVV session!")));
}
/*
GET https://kvv.imp.fu-berlin.de/sakai-login-tool/container
<- JSESSIONID
-> (Location-Header) https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO
?SAMLRequest=fZLLb.....Q8yre3X1IHwkJKE0Mnpy/V9TH4A
&RelayState=ss:mem:7ea01e29157b8bd906f7002176.....0d1a505f2c8bf
*/
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, 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);
}, error -> errorCallback.onError(new NetworkError(100110, error.networkResponse.statusCode, "Error on getting SAML request!")));
}
/*
GET https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO
?SAMLRequest=fZLLbsIwEEV/JfI+cWJAUIsgpbAoEi2IpF10UznxUKw6dupxaPn7hkdb2LD29bkzRzNGUeuGZ63fmjV8toA++K61QX58SEnrDLcCFXIjakDuK55njwvOopg3znpbWU2CDBGcV9ZMrcG2BpeD26kKnteLlGy9b5BT+rHbRapuok0bluC0MpEEmm9VWVoNfhshWnpgM7pa5gUJZt0wyogD9h+iJBiv/P6aomQTbtqSdhNtlIYzZg1SOag8zfMlCeazlLyNqpHsy1gO2V1fVsNBMuqJoUyAJaxXDUaiiyG2MDfohfEpYXEyDJM4ZKxgCe/FPI5fSbA6L36vjFTm/bal8hRC/lAUq/C02gs4PK7VBchkfHDNj8Xuwv5trPhVTiY3BeOf4DG96DmVNvypA89nK6tVtQ8yre3X1IHwkJKE0Mnpy/V9TH4A
&RelayState=ss:mem:7ea01e29157b8bd906f7002176213b6db5e1f45ebb88716a9820d1a505f2c8bf
-> JSESSIONID C4B6A428BA1F50746235D03F5D107A57
-> _idp_authn_lc_key 57a6ae26067f374cc3d0ccfc47e27b04b47752d2a3d4eb2782af0d3994535395
-> ROUTEID .1
*/
private void startIdentSession(String url, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
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;
}
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!")));
}
/*
POST https://identity.fu-berlin.de/idp-fub/Authn/UserPassword
<- j_username seedorf96
<- j_password neinhieristpatrick
<- (Header-"Content-Type") application/x-www-form-urlencoded
<- JSESSIONID
<- _idp_authn_lc_key
<- ROUTEID
-> _idp_session OTMuMTkzLjg1LjMz|LQ==|OGYxOWI4MjA2NTQ4YWUwYzJkOWM4Mjk4YzcwZDMwZmJiZjBmMTdmMzkyZGU2OWIwY2JkNmZlNjlmNTRmNzBlMQ==|wLlzQal7VqyntmG2vLNn06wt8wQ=
*/
private void loginIdent(final boolean first, String username, String password, String JSESSIONID, String _idp_authn_lc_key, String ROUTEID, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
HashMap<String, String> cookies = new HashMap<>();
cookies.put("JSESSIONID", JSESSIONID);
cookies.put("_idp_authn_lc_key", _idp_authn_lc_key);
cookies.put("ROUTEID", ROUTEID);
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, response -> {
if (first) {
callback.onResponse(new HashMap<>());
return;
}
String cookies1 = response.getHeaders().get("Set-Cookie");
if (cookies1 ==null) {
errorCallback.onError(new NetworkError(100131, -1, "Error on logging in to Identity Server!"));
return;
}
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!")));
}
/*
GET https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO
<- JSESSIONID
<- _idp_authn_lc_key
<- ROUTEID
<- _idp_session
-> (BODY) RelayState 7ea01e29157b8bd906f7002176213b6db5e1f45ebb88716a9820d1a505f2c8bf
-> (BODY) SAMLResponse PD94bWwgdmVyc2lvbj0...........wvc2FtbDJwOlJlc3BvbnNlPg==
*/
private void getSAMLResponse(String JSESSIONID, String _idp_authn_lc_key, String ROUTEID, String _idp_session, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
HashMap<String, String> cookies = new HashMap<>();
cookies.put("JSESSIONID", JSESSIONID);
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, response -> {
String body = response.getParsed();
if (body == null) {
errorCallback.onError(new NetworkError(100143, -1, "Error on getting SAML response!"));
return;
}
HashMap<String, String> object = new HashMap<>();
Pattern pattern = Pattern.compile("ss&#x3a;mem&#x3a;([0-9a-f]+)");
Matcher matcher = pattern.matcher(body);
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("name=\"SAMLResponse\" value=\"([0-9a-zA-Z+]+=*)");
matcher = pattern.matcher(body);
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!")));
}
/*
POST https://kvv.imp.fu-berlin.de/Shibboleth.sso/SAML2/POST
<- RelayState 7ea01e29157b8bd906f7002176213b6db5e1f45ebb88716a9820d1a505f2c8bf
<- SAMLResponse PD94bWwgdmVyc2lvbj0...........wvc2FtbDJwOlJlc3BvbnNlPg==
<- JSESSIONID
-> _shibsession_64656661756c7468747470733a2f2f6b76762e696d702e66752d6265726c696e2e64652f73686962626f6c657468
_b1912c5a03d733a80bd3fee772bf68d4
*/
private void loginKVV(String RelayState, String SAMLResponse, String JSESSIONID, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
HashMap<String, String> cookies = new HashMap<>();
cookies.put("JSESSIONID", JSESSIONID);
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, response -> {
String cookies1 = response.getHeaders().get("Set-Cookie");
if (cookies1 ==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(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!")));
}
/*
GET https://kvv.imp.fu-berlin.de/sakai-login-tool/container
<- JSESSIONID
<- _shibsession_64656661756c7468747470733a2f2f6b76762e696d702e66752d6265726c696e2e64652f73686962626f6c657468
_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(), response -> callback.onResponse(new HashMap<>()), error -> errorCallback.onError(new NetworkError(100160, error.networkResponse.statusCode, "Cannot finish login process!")));
}
private String getCookie(String cookies, String name) throws NoSuchFieldException {
Pattern pattern = Pattern.compile(name+"=([^;]+);");
Matcher matcher = pattern.matcher(cookies);
if (!matcher.find()) {
log.e("GETcookie failed", name);
log.e("GETcookie failed", cookies);
throw new NoSuchFieldException();
}
return matcher.group(1);
}
private HashMap<String, String> getCookie(String cookies, String[] names) throws NoSuchFieldException {
HashMap<String, String> result = new HashMap<>();
for (String name: names) {
result.put(name,this.getCookie(cookies, name));
}
return result;
}
}

View File

@@ -0,0 +1,43 @@
package de.sebse.fuplanner.services.NewKVV;
import android.content.Context;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
public class KVVModules {
private final HashMap<String, Object> addons = new HashMap<>();
private final KVVLogin login;
private final Context context;
public KVVModules(KVVLogin login, Context context) {
this.login = login;
this.context = context;
}
@NotNull
public KVVModulesAnnouncements announcements() {
return (KVVModulesAnnouncements) addAndGet("announcements", () -> new KVVModulesAnnouncements(login, list(), context));
}
@NotNull
public KVVModulesList list() {
return (KVVModulesList) addAndGet("list", () -> new KVVModulesList(login, context));
}
@NotNull
private Object addAndGet(@NotNull String addon, @NotNull ModuleCreatorInterface creatorInterface) {
Object o = addons.get(addon);
if (o == null) {
o = creatorInterface.create();
addons.put(addon, o);
}
return o;
}
private interface ModuleCreatorInterface {
@NotNull Object create();
}
}

View File

@@ -0,0 +1,88 @@
package de.sebse.fuplanner.services.NewKVV;
import android.content.Context;
import android.os.Build;
import android.text.Html;
import android.text.Spanned;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import de.sebse.fuplanner.services.NewKVV.types.Announcement;
import de.sebse.fuplanner.services.NewKVV.types.Modules;
import de.sebse.fuplanner.tools.network.NetworkCallback;
import de.sebse.fuplanner.tools.network.NetworkError;
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
class KVVModulesAnnouncements extends ModulesPart<ArrayList<Announcement>> {
KVVModulesAnnouncements(KVVLogin login, KVVModulesList list, Context context) {
super(login, list, context);
}
@Override
protected ArrayList<Announcement> getPart(Modules.Module module) {
return module.announcements;
}
@Override
protected boolean setPart(Modules.Module module, ArrayList<Announcement> part) {
boolean changed = false;
if (module.announcements != null && module.announcements.hashCode() != part.hashCode())
changed = true;
module.announcements = part;
return changed;
}
@Override
protected void upgrade(final String ID, final NetworkCallback<ArrayList<Announcement>> callback, final NetworkErrorCallback errorCallback) {
if (!login.isInOnlineMode()) {
errorCallback.onError(new NetworkError(101204, 500, "Currently running in offline mode!"));
return;
}
super.get(String.format("https://kvv.imp.fu-berlin.de/direct/announcement/site/%s.json?n=999999&d=999999999", ID), login.getLoginToken().getCookies(), response -> {
String body = response.getParsed();
if (body == null) {
errorCallback.onError(new NetworkError(101201, 403, "No announcements retrieved!"));
return;
}
ArrayList<Announcement> announcements = new ArrayList<>();
try {
JSONObject json = new JSONObject(body);
JSONArray sites = json.getJSONArray("announcement_collection");
for (int i = 0; i < sites.length(); i++) {
JSONObject site = sites.getJSONObject(i);
String id = site.getString("announcementId");
String title = site.getString("title");
String text = site.getString("body");
text = String.valueOf(fromHtml(text));
String createdBy = site.getString("createdByDisplayName");
long createdOn = site.getLong("createdOn");
// Extract attachment links
JSONArray attachments = site.getJSONArray("attachments");
ArrayList<String> urls = new ArrayList<>();
for (int j =0; j<attachments.length(); j++){
urls.add(attachments.getJSONObject(j).optString("url",null));
}
announcements.add(new Announcement(id, title, text, createdBy, createdOn, urls));
}
} catch (JSONException e) {
e.printStackTrace();
errorCallback.onError(new NetworkError(101202, 403, "Cannot parse announcements!"));
return;
}
// Empty announcements *may be* because token is invalid -> check
if (announcements.size() == 0)
login.testLoginToken(token -> callback.onResponse(announcements), errorCallback);
else
callback.onResponse(announcements);
}, error -> errorCallback.onError(new NetworkError(101203, error.networkResponse.statusCode, "Cannot get announcements!")));
}
}

View File

@@ -0,0 +1,161 @@
package de.sebse.fuplanner.services.NewKVV;
import android.content.Context;
import org.jetbrains.annotations.Nullable;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashSet;
import java.util.regex.MatchResult;
import de.sebse.fuplanner.services.NewKVV.types.Lecturer;
import de.sebse.fuplanner.services.NewKVV.types.Modules;
import de.sebse.fuplanner.tools.NewAsyncQueue;
import de.sebse.fuplanner.tools.Regex;
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 static de.sebse.fuplanner.services.NewKVV.ModulesPart.RETRY_COUNT;
public class KVVModulesList extends HTTPService {
protected final KVVLogin login;
@Nullable private Modules modules;
private NewAsyncQueue queue = new NewAsyncQueue();
KVVModulesList(KVVLogin login, Context context) {
super(context);
this.login = login;
restore();
}
public void find(String moduleID, NetworkCallback<Modules.Module> moduleNetworkCallback, NetworkErrorCallback errorCallback) {
find(moduleID, moduleNetworkCallback, errorCallback, RETRY_COUNT);
}
private void find(String moduleID, NetworkCallback<Modules.Module> moduleNetworkCallback, NetworkErrorCallback errorCallback, int retries) {
if (retries < 0) {
errorCallback.onError(new NetworkError(101107, -1, "Too many retries!"));
return;
}
if (this.modules != null) {
Modules.Module module = this.modules.get(moduleID);
if (module != null) {
moduleNetworkCallback.onResponse(module);
return;
}
}
recv(success -> find(moduleID, moduleNetworkCallback, errorCallback, retries - 1), errorCallback, true, RETRY_COUNT);
}
void store() {
if (this.modules != null) {
try {
this.modules.save(getContext());
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void restore() {
try {
this.modules = Modules.load(getContext());
} catch (FileNotFoundException ignored) {
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public void recv(final NetworkCallback<Modules> callback, final NetworkErrorCallback errorCallback) {
recv(callback, errorCallback, false, RETRY_COUNT);
}
public void recv(final NetworkCallback<Modules> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh, final int retries) {
queue.add(() -> {
if (this.modules != null && !forceRefresh) {
callback.onResponse(this.modules);
queue.next();
return;
}
this.upgrade(success -> {
if (this.modules == null)
this.modules = success;
else
this.modules.updateList(success);
callback.onResponse(this.modules);
queue.next();
}, error -> {
if (retries > 0 && (error.getHttpStatus() == 401 || error.getHttpStatus() == 403)) {
login.refreshLogin(success -> {
recv(callback, errorCallback, forceRefresh, retries-1);
queue.next();
}, errorCallback);
return;
}
errorCallback.onError(error);
queue.next();
});
});
}
private void upgrade(final NetworkCallback<Modules> callback, final NetworkErrorCallback errorCallback) {
if (!login.isInOnlineMode()) {
errorCallback.onError(new NetworkError(101105, 500, "Currently running in offline mode!"));
return;
}
// https://file.io/71sa2V
get("https://kvv.imp.fu-berlin.de/direct/site.json", login.getLoginToken().getCookies(), response -> {
String body = response.getParsed();
if (body == null) {
errorCallback.onError(new NetworkError(101101, 403, "No module list retrieved!"));
return;
}
Modules modules = new Modules(login.getLoginToken());
try {
JSONObject json = new JSONObject(body);
JSONArray sites = json.getJSONArray("site_collection");
for (int i = 0; i < sites.length(); i++) {
JSONObject site = sites.getJSONObject(i);
String semester = site.getJSONObject("props").getString("term_eid");
HashSet<String> lvNumbers = new HashSet<>();
for (MatchResult matchResult : Regex.allMatches("[0-9]+", site.getJSONObject("props").getString("kvv_lvnumbers"))) {
lvNumbers.add(matchResult.group());
}
String title = site.getString("entityTitle");
HashSet<Lecturer> lecturers = new HashSet<>();
for (String lecturer : site.getJSONObject("props").getString("kvv_lecturers").split("#")) {
if (lecturer.length() > 2)
lecturers.add(new Lecturer(lecturer));
}
String type = site.getJSONObject("props").getString("kvv_coursetype");
String description = site.getString("description");
description = String.valueOf(ModulesPart.fromHtml(description));
String id = site.getString("id");
modules.addModule(semester, lvNumbers, title, lecturers, type, description, id);
}
} catch (JSONException e) {
e.printStackTrace();
errorCallback.onError(new NetworkError(101102, 403, "Cannot parse module list!"));
return;
} catch (NoSuchFieldException e) {
e.printStackTrace();
errorCallback.onError(new NetworkError(101103, 403, "Cannot parse module list!"));
return;
}
// Empty module *may be* because token is invalid -> check
if (modules.size() == 0)
login.testLoginToken(token -> callback.onResponse(modules), errorCallback);
else
callback.onResponse(modules);
}, error -> errorCallback.onError(new NetworkError(101104, error.networkResponse.statusCode, "Cannot get module list!")));
}
}

View File

@@ -0,0 +1,75 @@
package de.sebse.fuplanner.services.NewKVV;
import android.content.Context;
import android.os.Build;
import android.text.Html;
import android.text.Spanned;
import de.sebse.fuplanner.services.NewKVV.types.Modules;
import de.sebse.fuplanner.tools.NewAsyncQueue;
import de.sebse.fuplanner.tools.network.HTTPService;
import de.sebse.fuplanner.tools.network.NetworkCallback;
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
public abstract class ModulesPart<T> extends HTTPService {
static final int RETRY_COUNT = 1;
protected final KVVLogin login;
protected final KVVModulesList list;
private NewAsyncQueue queue = new NewAsyncQueue();
ModulesPart(KVVLogin login, KVVModulesList list, Context context) {
super(context);
this.login = login;
this.list = list;
}
public void recv(final String moduleID, final NetworkCallback<Modules.Module> callback, final NetworkErrorCallback errorCallback) {
recv(moduleID, callback, errorCallback, false);
}
public void recv(final String moduleID, final NetworkCallback<Modules.Module> callback, final NetworkErrorCallback errorCallback, final boolean forceRefresh) {
list.find(moduleID, success -> recv(success, callback, errorCallback, forceRefresh, RETRY_COUNT), errorCallback);
}
private void recv(final Modules.Module module, final NetworkCallback<Modules.Module> callback, final NetworkErrorCallback errorCallback, final boolean forceRefresh, final int retries) {
queue.add(() -> {
if (getPart(module) != null && !forceRefresh) {
callback.onResponse(module);
queue.next();
return;
}
upgrade(module.getID(), success -> {
if (setPart(module, success)) {
this.list.store();
}
callback.onResponse(module);
queue.next();
}, error -> {
if (retries >= 0 && (error.getHttpStatus() == 401 || error.getHttpStatus() == 403)) {
login.refreshLogin(success -> {
recv(module, callback, errorCallback, forceRefresh, retries-1);
queue.next();
}, errorCallback);
return;
}
errorCallback.onError(error);
queue.next();
});
});
}
static Spanned fromHtml(String html){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY);
} else {
//noinspection deprecation
return Html.fromHtml(html);
}
}
protected abstract T getPart(Modules.Module module);
protected abstract boolean setPart(Modules.Module module, T part);
protected abstract void upgrade(final String ID, final NetworkCallback<T> callback, final NetworkErrorCallback errorCallback);
}

View File

@@ -0,0 +1,64 @@
package de.sebse.fuplanner.services.NewKVV.types;
import com.google.android.gms.common.internal.Objects;
import java.io.Serializable;
import java.util.ArrayList;
public class Announcement implements Serializable {
private final String id;
private final String title;
private final String body;
private final String createdBy;
private final long createdOn;
private final ArrayList<String> urls;
public Announcement(String id, String title, String body, String createdBy, long createdOn, ArrayList<String> urls) {
this.id = id;
this.title = title;
this.body = body;
this.createdBy = createdBy;
this.createdOn = createdOn;
this.urls = urls;
}
public ArrayList<String> getUrls() {
return urls;
}
private String getId() {
return id;
}
public String getTitle() {
return title;
}
public String getBody() {
return body;
}
public String getCreatedBy() {
return createdBy;
}
public long getCreatedOn() {
return createdOn;
}
@Override
public String toString() {
return "ID: "+getId()+
"\nTitle: "+getTitle()+
"\nBody length: "+getBody().length()+
"\nCreated by: "+getCreatedBy()+
"\nCreated on: "+getCreatedOn()+
"\nURLs: "+getUrls().toString();
}
@Override
public int hashCode() {
return Objects.hashCode(getId(), getBody(), getCreatedBy(), getCreatedOn(), getTitle(), getUrls());
}
}

View File

@@ -0,0 +1,53 @@
package de.sebse.fuplanner.services.NewKVV.types;
import java.io.Serializable;
import java.util.ArrayList;
public class Assignment implements Serializable {
private final String id;
private final String title;
private final long dueTime;
private final ArrayList<String> urls;
private final String instructions;
public Assignment(String id, String title, long dueTime, String gradebookItemName, String gradeScale, ArrayList<String> urls, String instructions) {//, String grade
this.id = id;
this.title = title;
this.dueTime = dueTime;
this.urls = urls;
//this.grade = grade;
this.instructions = instructions;
}
private String getId() {
return id;
}
public String getTitle() {
return title;
}
public boolean isOpen() {
return dueTime > System.currentTimeMillis();
}
public long getDueDate() {
return dueTime;
}
public ArrayList<String> getUrls() {
return urls;
}
public String getInstructions() {
return instructions;
}
@Override
public String toString() {
return "ID: "+getId()+
"\nTitle: "+getTitle()+
"\nDue date: "+getDueDate()+
"\nInstructions: "+getInstructions().substring(0, Math.min(getInstructions().length(), 100));
}
}

View File

@@ -0,0 +1,16 @@
package de.sebse.fuplanner.services.NewKVV.types;
import de.sebse.fuplanner.tools.DateSortedList;
public class AssignmentList extends DateSortedList<Assignment> {
@Override
public long getDateByItem(Assignment item) {
return item.getDueDate();
}
@Override
public boolean reversed() {
return true;
}
}

View File

@@ -0,0 +1,114 @@
package de.sebse.fuplanner.services.NewKVV.types;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import de.sebse.fuplanner.tools.ColorRGB;
public class Event implements Serializable {
private final String siteId;
private final String id;
private final String type;
private final String title;
private final long duration;
private final long firstTime;
private final String location;
public Event(String id, String type, String title, long duration, long firstTime, String siteId, String location) {
this.siteId = siteId;
this.id = id;
this.type = type;
this.title = title;
this.duration = duration;
this.firstTime = firstTime;
this.location = location
.replace(" Übungsraum", "")
.replace(" Konferenzraum", "")
.replace(" Seminarraum", "");
}
public String getId() {
return id;
}
public String getTitle() {
return this.title;
}
public String getType() {
return this.type;
}
public long getStartDate() {
return this.firstTime;
}
public long getEndDate() {
return this.firstTime+this.duration;
}
public String getLocation() {
return location;
}
public String getModuleId() {
return siteId;
}
public ColorRGB getColor() {
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
byte[] encodedHash;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
encodedHash = digest.digest(siteId.getBytes(StandardCharsets.UTF_8));
} else {
encodedHash = digest.digest(siteId.getBytes());
}
int h = (0xff & encodedHash[0]) + (0xff & encodedHash[1]) * 255;
h = h * 360 / 0xffff;
//int s = 0xff & encodedHash[2];
//s = s * 100 / 0xffff;
//int v = 0xff & encodedHash[3];
//v = v * 100 / 0xffff;
// range for more beautiful colors
h = h / 30 * 30;
int s = 100;
int v = 80;
return hsvToRgb(h/360.0, s/100.0, v/100.0);
}
private static ColorRGB hsvToRgb(double hue, double saturation, double value) {
int h = (int)(hue * 6);
double f = hue * 6 - h;
double p = value * (1 - saturation);
double q = value * (1 - f * saturation);
double t = value * (1 - (1 - f) * saturation);
switch (h) {
case 0: return new ColorRGB(value, t, p);
case 1: return new ColorRGB(q, value, p);
case 2: return new ColorRGB(p, value, t);
case 3: return new ColorRGB(p, q, value);
case 4: return new ColorRGB(t, p, value);
case 5: return new ColorRGB(value, p, q);
default: throw new RuntimeException("Something went wrong when converting from HSV to RGB. Input was " + hue + ", " + saturation + ", " + value);
}
}
@Override
public String toString() {
return "ID: "+getId()+
"\nType: "+getType()+
"\nStart Date: "+getStartDate()+
"\nEnd date: "+getEndDate();
}
}

View File

@@ -0,0 +1,16 @@
package de.sebse.fuplanner.services.NewKVV.types;
import de.sebse.fuplanner.tools.DateSortedList;
public class EventList extends DateSortedList<Event> {
@Override
public long getDateByItem(Event item) {
return item.getEndDate();
}
@Override
public boolean reversed() {
return false;
}
}

View File

@@ -0,0 +1,34 @@
package de.sebse.fuplanner.services.NewKVV.types;
import java.io.Serializable;
public class Gradebook implements Serializable {
private final String itemName;
private final double grade;
private final double maxPoints;
public Gradebook(String itemName, double points, double maxPoints) {
this.itemName = itemName;
this.grade = points;
this.maxPoints = maxPoints;
}
public double getMaxPoints() {
return maxPoints;
}
public double getPoints() {
return grade;
}
public String getItemName() {
return itemName;
}
@Override
public String toString() {
return "Name: "+getItemName()+
"\nPoints: "+ getPoints()+
"\nMax points: "+getMaxPoints();
}
}

View File

@@ -0,0 +1,128 @@
package de.sebse.fuplanner.services.NewKVV.types;
import android.annotation.SuppressLint;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import androidx.annotation.NonNull;
import de.sebse.fuplanner.tools.logging.Logger;
public class GroupedEvents {
private ArrayList<Group> arrayList = new ArrayList<>();
private Logger log = new Logger(this);
public void add(Event event) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(event.getStartDate());
long dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
for (Group group : arrayList) {
if (group.add(event)) {
return;
}
}
arrayList.add(new Group(event));
}
public List<Group> getGroups() {
return Collections.unmodifiableList(arrayList);
}
public class Group {
private long firstDate;
private long lastDate;
private ArrayList<Long> skippedDates;
private int dayOfWeek;
private long startTime;
private long duration;
private Group(Event event) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(event.getStartDate());
dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
startTime = calendar.get(Calendar.HOUR_OF_DAY)*3600000+calendar.get(Calendar.MINUTE)*60000+calendar.get(Calendar.SECOND)*1000+calendar.get(Calendar.MILLISECOND);
duration = event.getEndDate()-event.getStartDate();
skippedDates = new ArrayList<>();
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
firstDate = calendar.getTimeInMillis();
lastDate = firstDate;
}
private boolean add(Event event) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(event.getStartDate());
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
int startTime = calendar.get(Calendar.HOUR_OF_DAY)*3600000+calendar.get(Calendar.MINUTE)*60000+calendar.get(Calendar.SECOND)*1000+calendar.get(Calendar.MILLISECOND);
long length = event.getEndDate()-event.getStartDate();
if (this.dayOfWeek != dayOfWeek || this.startTime != startTime || this.duration != length)
return false;
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
long date = calendar.getTimeInMillis();
if (date < firstDate) {
firstDate = addDays(firstDate, -7);
while (firstDate > date) {
skippedDates.add(firstDate);
firstDate = addDays(firstDate, -7);
}
} else if (date > lastDate) {
lastDate = addDays(lastDate, 7);
while (lastDate < date) {
skippedDates.add(lastDate);
lastDate = addDays(lastDate, 7);
}
} else {
skippedDates.remove(date);
}
return true;
}
private long addDays(long timeInMillis, int days) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(timeInMillis);
calendar.add(Calendar.DAY_OF_YEAR, days);
return calendar.getTimeInMillis();
}
public long getFirstDate() {
return firstDate;
}
public long getLastDate() {
return lastDate;
}
public List<Long> getSkippedDates() {
return Collections.unmodifiableList(skippedDates);
}
public int getDayOfWeek() {
return dayOfWeek;
}
public long getStartTime() {
return startTime;
}
public long getDuration() {
return duration;
}
@SuppressLint("DefaultLocale")
@NonNull
@Override
public String toString() {
return String.format("%d - %d - %d - %s", dayOfWeek, startTime, duration, skippedDates);
}
}
}

View File

@@ -0,0 +1,41 @@
package de.sebse.fuplanner.services.NewKVV.types;
import java.io.Serializable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Lecturer implements Serializable {
private final String firstName;
private final String surname;
private final String mail;
public Lecturer(String parsableString) throws NoSuchFieldException {
Pattern pattern = Pattern.compile("([^|]*)\\|([^|]*)\\|([^|]*)\\|\\|", Pattern.DOTALL);
Matcher matcher = pattern.matcher(parsableString);
if (!matcher.find()) {
throw new NoSuchFieldException();
}
this.firstName = matcher.group(1);
this.surname = matcher.group(2);
this.mail = matcher.group(3);
}
private String getFirstName() {
return firstName;
}
private String getSurname() {
return surname;
}
private String getMail() {
return mail;
}
@Override
public String toString() {
return "First name: "+ getFirstName()+
"\nSurname: "+getSurname()+
"\nMail: "+getMail();
}
}

View File

@@ -0,0 +1,114 @@
package de.sebse.fuplanner.services.NewKVV.types;
import android.content.Context;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.HashMap;
import androidx.annotation.Nullable;
/**
* Created by sebastian on 29.10.17.
*/
public class LoginToken implements Serializable {
private static final String FILE_NAME = "LoginTokenSaveFile";
private final String username;
private final String shibsessionKey;
private final String shibsessionName;
private final String JSESSIONID;
@Nullable private String fullName;
@Nullable private String email;
public LoginToken(String username, String shibsessionKey, String shibsessionName, String JSESSIONID) {
this.username = username;
this.shibsessionKey = shibsessionKey;
this.shibsessionName = shibsessionName;
this.JSESSIONID = JSESSIONID;
}
@Nullable
public static LoginToken load(Context context) throws IOException, ClassNotFoundException {
FileInputStream fis;
try {
fis = context.openFileInput(FILE_NAME);
} catch (FileNotFoundException e) {
return null;
}
ObjectInputStream is = new ObjectInputStream(fis);
LoginToken loginToken = (LoginToken) is.readObject();
is.close();
fis.close();
return loginToken;
}
public void save(Context context) throws IOException {
FileOutputStream fos = context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(this);
os.close();
fos.close();
}
public void delete(Context context) {
context.deleteFile(FILE_NAME);
}
public void setAdditionals(String fullName, String email) {
this.fullName = fullName;
this.email = email;
}
public String getUsername() {
return username;
}
private String getShibsessionKey() {
return shibsessionKey;
}
private String getShibsessionName() {
return shibsessionName;
}
private String getJSESSIONID() {
return JSESSIONID;
}
public String getFullName() {
return fullName;
}
public String getEmail() {
return email;
}
public HashMap<String, String> getCookies() {
HashMap<String, String> cookies = new HashMap<>();
cookies.put("JSESSIONID", getJSESSIONID());
cookies.put(getShibsessionKey(), getShibsessionName());
cookies.put("pasystem_timezone_ok", "true");
return cookies;
}
public boolean isSameUser(LoginToken token) {
return token != null && this.getUsername().equals(token.getUsername());
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
HashMap<String, String> cookies = this.getCookies();
for (String header: cookies.keySet()) {
result.append(header).append("=").append(cookies.get(header)).append(";");
}
return result.substring(0, result.length()-1);
}
}

View File

@@ -0,0 +1,168 @@
package de.sebse.fuplanner.services.NewKVV.types;
import android.content.Context;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* Created by sebastian on 29.10.17.
*/
public class Modules implements Iterable<Modules.Module>, Serializable {
private SortedListModule list;
private final LoginToken token;
//private transient Logger log = new Logger(this);
private static final String FILE_NAME = "ModuleListSaveFile";
public Modules(LoginToken loginToken) {
this.token = loginToken;
this.list = new SortedListModule();
}
public void addModule(String semester, HashSet<String> lvNumber, String title, HashSet<Lecturer> lecturer, String type, String description, String ID) {
Module m = new Module(semester, lvNumber, title, lecturer, type, description, ID);
this.list.add(m);
}
@NonNull
@Override
public String toString() {
return this.list.toString();
}
@NonNull
@Override
public Iterator<Module> iterator() {
return this.list.iterator();
}
public Iterator<Module> latestSemesterIterator() {
return this.list.filteredIterator(this.list.getLatestSemester());
}
public int size() {
return this.list.size();
}
public Module get(String id) {
return this.list.getById(id);
}
public Module getByIndex(int index) {
return this.list.get(index);
}
public static Modules load(Context context) throws IOException, ClassNotFoundException {
FileInputStream fis = context.openFileInput(FILE_NAME);
ObjectInputStream is = new ObjectInputStream(fis);
Modules modules = (Modules) is.readObject();
is.close();
fis.close();
return modules;
}
public void save(Context context) throws IOException {
FileOutputStream fos = context.openFileOutput(FILE_NAME, Context.MODE_PRIVATE);
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(this);
os.close();
fos.close();
}
public void delete(Context context) {
context.deleteFile(FILE_NAME);
}
public LoginToken getToken() {
return token;
}
public void updateList(Modules modules) {
SortedListModule old = this.list;
this.list = modules.list;
for (Module oldModule : old) {
Module newModule = this.list.getById(oldModule.getID());
if (newModule != null) {
newModule.announcements = oldModule.announcements;
newModule.assignments = oldModule.assignments;
newModule.events = oldModule.events;
newModule.gradebook = oldModule.gradebook;
newModule.resources = oldModule.resources;
}
}
}
public class Module implements Serializable {
public final String semester;
final HashSet<String> lvNumber;
public final String title;
final HashSet<Lecturer> lecturer;
public final String type;
public final String description;
private final String ID;
@Nullable public ArrayList<Announcement> announcements;
@Nullable public AssignmentList assignments;
@Nullable public EventList events;
@Nullable public ArrayList<Gradebook> gradebook;
@Nullable public ArrayList<Resource> resources;
/*private Module() {
this(null, null, null, null, null);
throw new AssertionError("Do not use this constructor!");
}*/
public float getGradebookPercent(){
float maxPoint = 0;
float userPoint = 0;
if (gradebook != null) {
for (Gradebook g : gradebook){
maxPoint += g.getMaxPoints();
userPoint += g.getPoints();
}
}
if (maxPoint == 0)
return 0;
return userPoint/maxPoint;
}
private Module(String semester, HashSet<String> lvNumber, String title, HashSet<Lecturer> lecturer, String type, String description, String ID) {
semester = semester.replace("SS", "S");
semester = semester.replaceAll("[0-9]{2}([0-9]{2})", "$1");
title = title.replaceAll("(.*?) (S[0-9]{2}|W[0-9/]{5})", "$1");
this.semester = semester;
this.lvNumber = lvNumber;
this.title = title;
this.lecturer = lecturer;
this.type = type;
this.description = description;
this.ID = ID;
}
public String getID() {
return ID;
}
@NonNull
@Override
public String toString() {
return "Semester: "+semester+
"\nlvNumber: "+lvNumber.toString()+
"\ntitle: "+title+
"\nlecturer: "+lecturer.toString()+
"\ntype: "+type+
"\nID: "+ID;
}
}
}

View File

@@ -0,0 +1,134 @@
package de.sebse.fuplanner.services.NewKVV.types;
import java.io.Serializable;
import java.util.ArrayList;
import androidx.annotation.LayoutRes;
import de.sebse.fuplanner.R;
import de.sebse.fuplanner.tools.ui.treeview.LayoutItemType;
import de.sebse.fuplanner.tools.ui.treeview.TreeNode;
public abstract class Resource implements Serializable {
final String author;
final long modifiedDate;
final String title;
final String url;
private final boolean visible;
private final String container;
Resource(String author, String title, long modifiedDate, String url, boolean visible, String container) {
this.author = author;
this.title = title;
this.modifiedDate = modifiedDate;
this.url = url;
this.visible = visible;
this.container = container;
}
public String getAuthor() {
return author;
}
public long getModifiedDate() {
return modifiedDate;
}
public String getTitle() {
return title;
}
public String getUrl() {
return url;
}
public String getContainer() {
return container;
}
public boolean isVisible() {
return visible;
}
public abstract TreeNode getTreeNode();
public static class File extends Resource implements LayoutItemType {
private final String type;
public File(String author, String title, long modifiedDate, String url, boolean visible, String container, String type) {
super(author, title, modifiedDate, url, visible, container);
this.type = type;
}
@Override
public String toString() {
return "Resource{" +
"author='" + author + '\'' +
", modifiedDate=" + modifiedDate +
", title='" + title + '\'' +
", url='" + url + '\'' +
", type='" + type + '\'' +
'}';
}
@Override
public TreeNode getTreeNode() {
return new TreeNode<>(this);
}
@Override
public @LayoutRes int getLayoutId() {
return R.layout.item_file;
}
}
public static class Folder extends Resource implements LayoutItemType {
private final ArrayList<Resource> children;
public Folder(String author, String title, long modifiedDate, String url, boolean visible, String container) {
super(author, title, modifiedDate, url, visible, container);
children = new ArrayList<>();
}
public void add(Resource res){
children.add(res);
}
public Resource get(int id){
return children.get(id);
}
public int size(){
return children.size();
}
@Override
public String toString() {
return "Resource{" +
"author='" + author + '\'' +
", modifiedDate=" + modifiedDate +
", title='" + title + '\'' +
", url='" + url + '\'' +
", children='" + children + '\'' +
'}';
}
@Override
public TreeNode getTreeNode() {
TreeNode dir = new TreeNode<>(this);
for (Resource res: children) {
dir.addChild(res.getTreeNode());
}
return dir;
}
@Override
public @LayoutRes int getLayoutId() {
return R.layout.item_dir;
}
}
}

View File

@@ -0,0 +1,68 @@
package de.sebse.fuplanner.services.NewKVV.types;
import de.sebse.fuplanner.tools.Regex;
import de.sebse.fuplanner.tools.SortedList;
public class SortedListModule extends SortedList<Modules.Module, String, String> {
private static final int LARGER = 1;
private static final int EQUAL = 0;
private static final int SMALLER = -1;
@Override
public int compare(Modules.Module o1, Modules.Module o2) {
int semester;
try {
semester = -compareSemester(o1.semester, o2.semester);
} catch (NoSuchFieldException e) {
e.printStackTrace();
semester = EQUAL;
}
if (semester != EQUAL)
return semester;
return o1.title.compareToIgnoreCase(o2.title);
}
@Override
public void add(Modules.Module e) {
super.add(e);
}
public String getLatestSemester() {
if (size() > 0)
//noinspection ConstantConditions
return this.get(0).semester;
else
return null;
}
private static int compareSemester(String a, String b) throws NoSuchFieldException {
if (a == null && b == null)
return EQUAL;
if (a == null)
return SMALLER;
if (b == null)
return LARGER;
String s1type = Regex.regex("^(S|WS) ", a);
int s1year = Integer.parseInt(Regex.regex("^(S|WS) ([0-9]{2})", a, 2));
String s2type = Regex.regex("^(S|WS) ", b);
int s2year = Integer.parseInt(Regex.regex("^(S|WS) ([0-9]{2})", b, 2));
if (s1year == s2year) {
if (s1type.equals(s2type))
return EQUAL;
return s1type.equals("S") ? SMALLER : LARGER;
}
return s1year < s2year ? SMALLER : LARGER;
}
@Override
public boolean hasIdentifier(Modules.Module o1, String id) {
return o1.getID().equals(id);
}
@Override
public boolean hasFilter(Modules.Module o1, String filter) {
return o1.semester.equals(filter);
}
}

View File

@@ -28,6 +28,4 @@ public interface MainActivityListener {
void addRequestPermissionsResultListener(RequestPermissionsResultListener listener, String id); void addRequestPermissionsResultListener(RequestPermissionsResultListener listener, String id);
void removeRequestPermissionsResultListener(String id); void removeRequestPermissionsResultListener(String id);
void refreshNavigation();
} }

View File

@@ -0,0 +1,58 @@
package de.sebse.fuplanner.tools;
import java.util.LinkedList;
import de.sebse.fuplanner.tools.network.NetworkCallback;
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
public class NewAsyncQueue {
private final LinkedList<AsyncQueueCallback> mQueue = new LinkedList<>();
private boolean mIsRunning = false;
public void add(AsyncQueueCallback callback) {
if (isRunning())
getQueue().addLast(callback);
else {
setRunning(true);
callback.run();
}
}
public void next() {
AsyncQueueCallback callback = getQueue().pollFirst();
if (callback == null)
setRunning(false);
else
callback.run();
}
public interface AsyncQueueCallback {
void run();
}
public NetworkErrorCallback check(NetworkErrorCallback value) {
return error -> {
value.onError(error);
next();
};
}
public <T> NetworkCallback<T> check(NetworkCallback<T> value) {
return success -> {
value.onResponse(success);
next();
};
}
private boolean isRunning() {
return mIsRunning;
}
private void setRunning(boolean value) {
mIsRunning = value;
}
private LinkedList<AsyncQueueCallback> getQueue() {
return mQueue;
}
}

View File

@@ -10,6 +10,7 @@
<include <include
layout="@layout/app_bar_main" layout="@layout/app_bar_main"
android:id="@+id/app_bar_include"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_files" path="."/>
</paths>