diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..c08c40103184e14d708f01fa435b142e685236f4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +*.iml +.gradle +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..956c004dc0ac315b0acae3a06888ea54bd6890f5 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,2 @@ +/build +/release \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..2ea288ce9b55c6f3e7ea080d19230e76e1747e72 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,65 @@ +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion 29 + buildToolsVersion "30.0.2" + + defaultConfig { + applicationId "fr.nitorac.aurionweb" + minSdkVersion 19 + targetSdkVersion 29 + versionCode 1 + versionName "1.0.0" + multiDexEnabled true + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + compileOptions { + coreLibraryDesugaringEnabled true + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: "libs", include: ["*.jar"]) + implementation 'androidx.appcompat:appcompat:1.2.0' + implementation 'com.google.android.material:material:1.2.1' + implementation 'androidx.constraintlayout:constraintlayout:2.0.1' + implementation 'androidx.vectordrawable:vectordrawable:1.1.0' + implementation 'androidx.navigation:navigation-fragment:2.3.0' + implementation 'androidx.navigation:navigation-ui:2.3.0' + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' + implementation 'org.jsoup:jsoup:1.13.1' + implementation 'com.google.code.gson:gson:2.8.6' + implementation 'org.projectlombok:lombok:1.18.12' + annotationProcessor 'org.projectlombok:lombok:1.18.12' + + implementation 'com.github.lzyzsd:circleprogress:1.2.1' + implementation 'com.mindorks.android:prdownloader:0.6.0' + + implementation 'com.afollestad.material-dialogs:core:3.3.0' + implementation 'com.afollestad.material-dialogs:bottomsheets:3.3.0' + + testAnnotationProcessor 'org.projectlombok:lombok:1.18.12' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' + + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10' + implementation "androidx.core:core-ktx:1.3.2" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} +repositories { + mavenCentral() +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000000000000000000000000000000000000..481bb434814107eb79d7a30b676d344b0df2f8ce --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/fr/nitorac/aurionweb/ExampleInstrumentedTest.java b/app/src/androidTest/java/fr/nitorac/aurionweb/ExampleInstrumentedTest.java new file mode 100644 index 0000000000000000000000000000000000000000..78a697fe8f47cd70a621c84625862550f96e02ac --- /dev/null +++ b/app/src/androidTest/java/fr/nitorac/aurionweb/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package fr.nitorac.aurionweb; + +import android.content.Context; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertEquals; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("fr.nitorac.aurionweb", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000000000000000000000000000000000..f77c85588bcbeb5cbb7989c8cae0cb523e9749b0 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="fr.nitorac.aurionweb"> + + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission-sdk-23 android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> + <uses-permission-sdk-23 android:name="android.permission.WAKE_LOCK" /> + <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/> + <uses-permission-sdk-23 android:name="android.permission.REQUEST_INSTALL_PACKAGES"/> + + <application + android:name=".AurionApp" + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:roundIcon="@mipmap/ic_launcher_round" + android:supportsRtl="true" + android:theme="@style/AppTheme"> + <activity + android:name=".MainActivity" + android:label="@string/app_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <provider + android:name="androidx.core.content.FileProvider" + android:authorities="${applicationId}.provider" + android:exported="false" + android:grantUriPermissions="true"> + <meta-data + android:name="android.support.FILE_PROVIDER_PATHS" + android:resource="@xml/provider_paths"/> + </provider> + </application> + +</manifest> \ No newline at end of file diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..32a3deb7f2521963368bbbe9ac18e4e6e31f4ea2 Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/fr/nitorac/aurionweb/AurionApp.java b/app/src/main/java/fr/nitorac/aurionweb/AurionApp.java new file mode 100644 index 0000000000000000000000000000000000000000..f7ab6790123a5f4daf623a9f1e55158ee8ee18dd --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/AurionApp.java @@ -0,0 +1,18 @@ +package fr.nitorac.aurionweb; + +import android.app.Application; + +import com.downloader.PRDownloader; +import com.downloader.PRDownloaderConfig; + +public class AurionApp extends Application { + @Override + public void onCreate() { + super.onCreate(); + PRDownloader.initialize(getApplicationContext(), PRDownloaderConfig.newBuilder() + .setReadTimeout(30_000) + .setConnectTimeout(30_000) + .setUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0") + .build()); + } +} diff --git a/app/src/main/java/fr/nitorac/aurionweb/MainActivity.java b/app/src/main/java/fr/nitorac/aurionweb/MainActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..37e137f1e07bfc30a35c7ecb9b9b131b32749b98 --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/MainActivity.java @@ -0,0 +1,70 @@ +package fr.nitorac.aurionweb; + +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; +import androidx.navigation.ui.NavigationUI; + +import com.google.android.material.bottomnavigation.BottomNavigationView; + +import fr.nitorac.aurionweb.aurionweb.AurionManager; +import fr.nitorac.aurionweb.update.UpdateManager; + +public class MainActivity extends AppCompatActivity { + + private static MainActivity mainActivity; + + @Override + protected void onCreate(Bundle savedInstanceState) { + mainActivity = this; + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + UpdateManager.getInstance().checkUpdates(this); + AurionManager.getInstance().spawnLoginDialog(this); + + BottomNavigationView navView = findViewById(R.id.nav_view); + NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment); + NavigationUI.setupWithNavController(navView, navController); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.appbar_menu, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.connect_menuitem: + AurionManager.getInstance().spawnLoginDialog(this); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + + @Override + protected void onPause() { + super.onPause(); + UpdateManager.handleOnPause(); + } + + @Override + protected void onResume() { + super.onResume(); + UpdateManager.handleOnResume(); + } + + public static MainActivity getInstance(){ + return mainActivity; + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/nitorac/aurionweb/Utils.java b/app/src/main/java/fr/nitorac/aurionweb/Utils.java new file mode 100644 index 0000000000000000000000000000000000000000..c2fb787a8cf36bad30ee21ef003da83214733ec4 --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/Utils.java @@ -0,0 +1,96 @@ +package fr.nitorac.aurionweb; + +import android.content.Context; +import android.graphics.Point; +import android.view.Display; +import android.view.WindowManager; + +import com.google.gson.JsonObject; + +import org.jsoup.Connection; +import org.jsoup.Jsoup; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.text.CharacterIterator; +import java.text.Normalizer; +import java.text.StringCharacterIterator; +import java.util.Locale; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; + +public class Utils { + + private static SSLContext sslContext; + + public static Connection wrapSSL(String url) throws GeneralSecurityException, IOException { + return Jsoup.connect(url) + .sslSocketFactory(getContextForTrustedCertificates(MainActivity.getInstance().getApplicationContext()).getSocketFactory()); + } + + public static SSLContext getContextForTrustedCertificates(Context c) throws GeneralSecurityException, IOException { + if(sslContext == null){ + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + + + Certificate cas = cf.generateCertificate(c.getResources().openRawResource(R.raw.cas_ensiie_fr)); + Certificate aurionweb = cf.generateCertificate(c.getResources().openRawResource(R.raw.aurionweb_ensiie_fr)); + + // Create a KeyStore containing our trusted CAs + String keyStoreType = KeyStore.getDefaultType(); + KeyStore keyStore = KeyStore.getInstance(keyStoreType); + keyStore.load(null, null); + keyStore.setCertificateEntry("cas", cas); + keyStore.setCertificateEntry("aurionweb", aurionweb); + + // Create a TrustManager that trusts the CAs in our KeyStore + String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); + tmf.init(keyStore); + + // Create an SSLContext that uses our TrustManager + sslContext = SSLContext.getInstance("TLS"); + sslContext.init(null, tmf.getTrustManagers(), null); + } + + return sslContext; + } + + public static Point getScreenSize(Context c){ + WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE); + Display display = wm.getDefaultDisplay(); + Point size = new Point(); + display.getSize(size); + return size; + } + public static JsonObject strToJson(String text) { + JsonObject root = new JsonObject(); + root.addProperty("text", text); + return root; + } + + public static String stripAccents(String s) { + s = Normalizer.normalize(s, Normalizer.Form.NFD); + s = s.replaceAll("[\\p{InCombiningDiacriticalMarks}]", ""); + return s; + } + + public static String bytesToString(long bytes) { + long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes); + if (absB < 1024) { + return bytes + " o"; + } + long value = absB; + CharacterIterator ci = new StringCharacterIterator("KMGTPE"); + for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) { + value >>= 10; + ci.next(); + } + value *= Long.signum(bytes); + return String.format(Locale.FRENCH, "%.1f %co", value / 1024.0, ci.current()); + } +} diff --git a/app/src/main/java/fr/nitorac/aurionweb/aurionweb/AurionManager.java b/app/src/main/java/fr/nitorac/aurionweb/aurionweb/AurionManager.java new file mode 100644 index 0000000000000000000000000000000000000000..71bed7109a2a0964450a14c6d32a31f9cf31d41b --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/aurionweb/AurionManager.java @@ -0,0 +1,179 @@ +package fr.nitorac.aurionweb.aurionweb; + +import android.content.Context; +import android.util.TypedValue; +import android.widget.EditText; +import android.widget.Toast; + +import androidx.core.content.ContextCompat; + +import com.afollestad.materialdialogs.LayoutMode; +import com.afollestad.materialdialogs.MaterialDialog; +import com.afollestad.materialdialogs.bottomsheets.BottomSheet; +import com.afollestad.materialdialogs.bottomsheets.BottomSheetsKt; +import com.afollestad.materialdialogs.customview.DialogCustomViewExtKt; + +import org.jsoup.Connection; +import org.jsoup.nodes.Document; + +import java.io.IOException; +import java.security.GeneralSecurityException; + +import fr.nitorac.aurionweb.R; +import fr.nitorac.aurionweb.Utils; + +public class AurionManager { + + private static AurionManager instance; + + public AurionManager(){ + + } + + public boolean checkPassword(String username, String password) { + AurionSession session = new AurionSession(); + try { + initConnection(session, username, password); + return true; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + // Instancie une connection vers AurionWeb + public Document initConnection(AurionSession AurionSession, String username, String pwd) throws IOException, GeneralSecurityException { + // Première connection pour récupérer un String "execution" nécessaire à la connection + Connection.Response initRes = Utils.wrapSSL("https://cas.ensiie.fr/login?service=https%3A%2F%2Faurionweb.ensiie.fr%2F%2Flogin%2Fcas") + .method(Connection.Method.GET) + .header("Cache-Control", "max-age=0") + .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36") + .execute(); + String execution = initRes.parse().getElementsByAttributeValue("name", "execution").get(0).attr("value"); + //Requête effective permettant la validation de l'authentification sur CAS + Connection secConn = Utils.wrapSSL("https://cas.ensiie.fr/login?service=https%3A%2F%2Faurionweb.ensiie.fr%2F%2Flogin%2Fcas") + .method(Connection.Method.POST) + .followRedirects(true) + .header("Cache-Control", "max-age=0") + .header("Content-Type", "application/x-www-form-urlencoded") + .header("Host", "cas.ensiie.fr") + .header("Origin", "https://cas.ensiie.fr") + .header("Referer", "https://cas.ensiie.fr/login?service=https%3A%2F%2Faurionweb.ensiie.fr%2F%2Flogin%2Fcas") + .header("Sec-Fetch-Mode", "navigate") + .header("Sec-Fetch-Site", "same-origin") + .header("Upgrade-Insecure-Requests", "1") + .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36") + .data("username", username) + .data("_eventId", "submit") + //.data("geolocation", "") + .data("execution", execution); + secConn.data("password", pwd); + Connection.Response sec = secConn.execute(); + Document doc1 = sec.parse(); + AurionSession.parseViewState(doc1); + AurionSession.setJSESSIONID(sec.cookie("JSESSIONID")); + //On trouve le form ID + AurionSession.parseConnectMenuFormId(doc1); + //On trouve l'ID du bouton Emploi du temps (pour les requêtes) + AurionSession.parseConnectMenuId(doc1); + //Requête effective permettant la validation de l'authentification sur CAS + Connection.Response tmp = Utils.wrapSSL("https://aurionweb.ensiie.fr/faces/MainMenuPage.xhtml") + .method(Connection.Method.POST) + .followRedirects(true) + .header("Cache-Control", "max-age=0") + .header("Content-Type", "application/x-www-form-urlencoded") + .header("Host", "aurionweb.ensiie.fr") + .header("Origin", "https://aurionweb.ensiie.fr") + .header("Referer", "https://aurionweb.ensiie.fr/") + .header("Faces-Request", "partial/ajax") + .header("Upgrade-Insecure-Requests", "1") + .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36") + .cookie("JSESSIONID", AurionSession.getJSESSIONID()) + .data("javax.faces.partial.ajax", "true") + .data("javax.faces.source", AurionSession.getMenuFormId()) + .data("javax.faces.partial.execute", AurionSession.getMenuFormId()) + .data("javax.faces.partial.render", "form:sidebar") + .data(AurionSession.getMenuFormId(), AurionSession.getMenuFormId()) + .data("webscolaapp.Sidebar.ID_SUBMENU", AurionSession.getMenuId()) + .data("form", "form") + //.data("form:largeurDivCenter", "1219") + //.data("form:sauvegarde", "") + .data("javax.faces.ViewState", AurionSession.getViewState()) + .execute(); + Document doc2 = tmp.parse(); + AurionSession.parseViewState(doc2); + AurionSession.parseConnectSubmenuId(doc2); + // On charge une fois la page pour valider un affichage fictif (cote server aurion) + // Requête effective permettant la validation de l'authentification sur CAS + Connection.Response resp = Utils.wrapSSL("https://aurionweb.ensiie.fr/faces/MainMenuPage.xhtml") + .method(Connection.Method.POST) + .followRedirects(true) + .header("Cache-Control", "max-age=0") + .header("Content-Type", "application/x-www-form-urlencoded") + .header("Host", "aurionweb.ensiie.fr") + .header("Origin", "https://aurionweb.ensiie.fr") + .header("Referer", "https://aurionweb.ensiie.fr/") + .header("Upgrade-Insecure-Requests", "1") + .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36") + .cookie("JSESSIONID", AurionSession.getJSESSIONID()) + .data("form", "form") + //.data("form:largeurDivCenter", "1219") + //.data("form:sauvegarde", "") + .data("form:sidebar", "form:sidebar") + .data("form:sidebar_menuid", AurionSession.getSubmenuId()) + .data("javax.faces.ViewState", AurionSession.getViewState()) + .execute(); + Document respDoc = resp.parse(); + //On trouve le formId du ChoixPlanning + AurionSession.parseChoixPlanningFormId(respDoc); + //On trouve l'id du bouton 'Voir planning' necéssaire aux requêtes + AurionSession.parseChoixPlanningSubmitBtnId(respDoc); + AurionSession.parseViewState(respDoc); + return respDoc; + } + + public void spawnLoginDialog(Context c){ + final MaterialDialog dialog = new MaterialDialog(c, new BottomSheet(LayoutMode.MATCH_PARENT)) + .title(null, "Connexion AurionWeb") + .icon(null, ContextCompat.getDrawable(c, R.drawable.ic_login)) + .cancelable(true) + .noAutoDismiss() + .positiveButton(null, "CONNEXION", materialDialog -> { + EditText login = materialDialog.getView().findViewById(R.id.loginInput); + EditText pwd = materialDialog.getView().findViewById(R.id.passwordInput); + CheckConnectionTask checkTask = new CheckConnectionTask(c, (res) -> { + if(res){ + Toast.makeText(c, "Connexion réussie !", Toast.LENGTH_LONG).show(); + materialDialog.dismiss(); + }else{ + Toast.makeText(c, "Mauvais mot de passe ou erreur de connexion !", Toast.LENGTH_LONG).show(); + } + }); + checkTask.execute(login.getText().toString().toLowerCase().trim(), pwd.getText().toString().trim()); + return null; + }) + .negativeButton(null, "ANNULER", materialDialog -> { + materialDialog.dismiss(); + return null; + }); + BottomSheetsKt.setPeekHeight(dialog, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 400 /*dp*/, c.getResources().getDisplayMetrics()), null); + + // Errk, dirtyyyy, but I can't find any other way to do it + DialogCustomViewExtKt.customView(dialog, R.layout.dialog_login, null, false, false, true, true); + + // Errk, dirtyyyy, workaround to avoid a flickering issue when dialog is sliding + dialog.dismiss(); + + dialog.show(); + } + + + + public static AurionManager getInstance(){ + if(instance == null){ + instance = new AurionManager(); + } + + return instance; + } +} diff --git a/app/src/main/java/fr/nitorac/aurionweb/aurionweb/AurionSession.java b/app/src/main/java/fr/nitorac/aurionweb/aurionweb/AurionSession.java new file mode 100644 index 0000000000000000000000000000000000000000..996e47dfe6737b6db1c9d051270e07bbce2e1937 --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/aurionweb/AurionSession.java @@ -0,0 +1,178 @@ +package fr.nitorac.aurionweb.aurionweb; + +import android.util.Log; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.nodes.Node; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import fr.nitorac.aurionweb.Utils; +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@NoArgsConstructor +@Data +public class AurionSession { + private static final String TAG = "AurionSession"; + + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private static final Pattern menuIdPattern = Pattern.compile("\\{PrimeFaces\\.ab\\(\\{s:\"(.*?)\"", Pattern.MULTILINE); + + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private static final Pattern liSubmenuPattern = Pattern.compile("'form:sidebar_menuid':'(.*?)'", Pattern.MULTILINE); + private static final List<String> elemsPopupLabel = Arrays.asList("cour", "interve", "ressourc"); + + private String JSESSIONID = ""; + + //Variables ID pour la connection et le menu (@see initConnection du AurionManager) + @Setter(AccessLevel.PRIVATE) + private String viewState = ""; + @Setter(AccessLevel.NONE) + private String menuFormId = ""; + @Setter(AccessLevel.NONE) + private String menuId = ""; + + //Variables ID pour le ChoixPlanning + @Setter(AccessLevel.NONE) + private String submenuId = ""; + @Setter(AccessLevel.NONE) + private String choixpFormId = ""; + + //Variables ID pour le Planning + @Setter(AccessLevel.NONE) + private String choixpSubmitId = ""; + @Setter(AccessLevel.NONE) + private String planningFormId = ""; + //Variables ID pour le popup d'un événement du Planning : Cours, Intervenants, Ressources, et Infos + @Setter(AccessLevel.NONE) + private ArrayList<String> elemsPopupId = new ArrayList<>(3); + private Document lastDoc; + + private static String getViewState(Document doc) { + Element view = doc.getElementById("j_id1:javax.faces.ViewState:0"); + if (view == null) { + return null; + } + String test = view.attr("value"); + return test.isEmpty() ? /* CDATA Node */view.text() : test; + } + + public void parseViewState(Document doc) { + doc = setLastDoc(doc); + String viewState = getViewState(doc); + if (viewState == null) { + Log.e(TAG, "Impossible d'analyser le ViewState pour : \n" + doc.outerHtml()); + } + setViewState(viewState); + } + + public boolean parseConnectMenuFormId(Document doc) { + doc = setLastDoc(doc); + //On va récupérer l'ID Aurionweb qui correspond à la form du menu + String menuScript = doc.getElementsByAttributeValue("type", "text/javascript").stream().map(Node::outerHtml).filter(e -> e.contains("chargerSousMenu = function()")).findFirst().orElse(""); + Matcher menuMatcher = menuIdPattern.matcher(menuScript); + menuFormId = menuMatcher.find() ? menuMatcher.group(1) : null; + if (menuFormId == null) { + Log.e(TAG, "Impossible de trouver l'ID menu dans : " + menuScript); + return false; + } + return true; + } + + public boolean parseConnectMenuId(Document doc) { + doc = setLastDoc(doc); + //Maintenant on va récupérer l'ID du menu qu'on souhaite, c'est à dire Emploi du temps + // (Oui c'est bien un menu même si on cherche une balise avec submenu_... comme classe x) (c'est aurionweb) + Element menuClass = doc.getElementsByAttributeValueContaining("class", "submenu_").stream().filter(e -> e.text().contains("Emploi du temps")).findFirst().orElse(null); + menuId = menuClass != null ? menuClass.classNames().stream().filter(c -> c.startsWith("submenu_")).findFirst().orElse("") : null; + if (menuId == null) { + Log.e(TAG, "Impossible de trouver un menu avec 'Emploi du temps' comme texte !"); + return false; + } + return true; + } + + public boolean parseConnectSubmenuId(Document doc) { + doc = setLastDoc(doc); + //On va maintenant trouver un ID de la forme '2_1' qui correspond à l'ID du lien "Planning d'un étudiant au choix" + Element liSubmenuID = Jsoup.parse(doc.getElementById("form:sidebar").text()).getElementsByTag("li").stream().filter(e -> Utils.stripAccents(e.text()).equalsIgnoreCase("Planning d'un etudiant au choix")).findFirst().orElse(null); + if (liSubmenuID != null) { + Matcher submenuMatcher = liSubmenuPattern.matcher(liSubmenuID.child(0).attr("onclick")); + if (submenuMatcher.find()) { + submenuId = submenuMatcher.group(1); + return true; + } + } else { + Log.e(TAG, "Impossible de trouver un sous menu de tag 'li' avec 'Planning d'un étudiant au choix' comme texte !"); + } + return false; + } + + + public boolean parseChoixPlanningFormId(Document doc) { + doc = setLastDoc(doc); + //On va trouver l'ID principal correspondant à la form de la page ChoixPlanning d'Aurionweb + Element formDiv = doc.getElementsByAttributeValueContaining("class", "ui-datatable ui-widget").first(); + if (formDiv != null) { + choixpFormId = formDiv.id(); + return true; + } + return false; + } + + public boolean parseChoixPlanningSubmitBtnId(Document doc) { + doc = setLastDoc(doc); + //On va trouver l'ID du bouton 'Voir planning' qui est nécessaire dans les données d'une requête accédant à l'edt d'un user + Element submitBtn = doc.getElementsByAttributeValue("type", "submit").stream().filter(e -> e.text().toLowerCase().contains("voir planning")).findFirst().orElse(null); + if (submitBtn != null) { + choixpSubmitId = submitBtn.id(); + return true; + } + return false; + } + + public boolean parsePlanningFormId(Document doc) { + doc = setLastDoc(doc); + //On va trouver la form id principale de la page + Element scheduleDiv = doc.getElementsByClass("schedule").first(); + if (scheduleDiv != null) { + planningFormId = scheduleDiv.id(); + return true; + } + return false; + } + + public void parseEventProps(Document doc) { + elemsPopupId.clear(); + for (int i = 0; i < elemsPopupLabel.size(); i++) { + elemsPopupId.add(null); + } + for (Element e : doc.getElementsByTag("a")) { + if (e.children().size() == 0) { + for (int i = 0; i < elemsPopupLabel.size(); i++) { + if (e.text().toLowerCase().contains(elemsPopupLabel.get(i))) { + //substring(1) pour retirer le # devant + elemsPopupId.set(i, doc.getElementById(e.attr("href").substring(1)).getElementsByAttributeValueContaining("id", "_data").first().id()); + } + } + } + } + } + + + public Document setLastDoc(Document doc) { + return doc == null ? getLastDoc() : (lastDoc = doc); + } +} diff --git a/app/src/main/java/fr/nitorac/aurionweb/aurionweb/AurionView.java b/app/src/main/java/fr/nitorac/aurionweb/aurionweb/AurionView.java new file mode 100644 index 0000000000000000000000000000000000000000..1b13e02b19336e43d85c4469768e8d2a64837414 --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/aurionweb/AurionView.java @@ -0,0 +1,7 @@ +package fr.nitorac.aurionweb.aurionweb; + +public class AurionView { + public AurionView(){ + + } +} diff --git a/app/src/main/java/fr/nitorac/aurionweb/aurionweb/CheckConnectionTask.java b/app/src/main/java/fr/nitorac/aurionweb/aurionweb/CheckConnectionTask.java new file mode 100644 index 0000000000000000000000000000000000000000..974ddb5088325ce130d3c84c8d718c2be58324bc --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/aurionweb/CheckConnectionTask.java @@ -0,0 +1,50 @@ +package fr.nitorac.aurionweb.aurionweb; + +import android.content.Context; +import android.os.AsyncTask; + +import com.afollestad.materialdialogs.LayoutMode; +import com.afollestad.materialdialogs.MaterialDialog; +import com.afollestad.materialdialogs.bottomsheets.BottomSheet; +import com.afollestad.materialdialogs.customview.DialogCustomViewExtKt; + +import fr.nitorac.aurionweb.R; + +public class CheckConnectionTask extends AsyncTask<String, Void, Boolean> { + + private MaterialDialog loading; + private ConnectionCheck callback; + + public CheckConnectionTask(Context c, ConnectionCheck callback){ + this.callback = callback; + loading = new MaterialDialog(c, new BottomSheet(LayoutMode.WRAP_CONTENT)) + .cancelable(false) + .cancelOnTouchOutside(false) + .title(R.string.loading, null) + .noAutoDismiss(); + DialogCustomViewExtKt.customView(loading, R.layout.dialog_loading, null, false, false, true, true); + loading.dismiss(); + } + + @Override + protected void onPreExecute() { + super.onPreExecute(); + loading.show(); + } + + @Override + protected void onPostExecute(Boolean res) { + super.onPostExecute(res); + loading.dismiss(); + callback.onResult(res); + } + + @Override + protected Boolean doInBackground(String... creds) { + return AurionManager.getInstance().checkPassword(creds[0], creds[1]); + } + + public interface ConnectionCheck { + void onResult(Boolean res); + } +} diff --git a/app/src/main/java/fr/nitorac/aurionweb/security/SecurityHelper.java b/app/src/main/java/fr/nitorac/aurionweb/security/SecurityHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..f73a917b0c464749c482a86f8122561d004795ff --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/security/SecurityHelper.java @@ -0,0 +1,11 @@ +package fr.nitorac.aurionweb.security; + +import java.io.IOException; +import java.security.GeneralSecurityException; + +public interface SecurityHelper { + + void init() throws GeneralSecurityException, IOException; + String decrypt(String encrypted) throws GeneralSecurityException, IOException; + String encrypt(String raw) throws GeneralSecurityException, IOException; +} diff --git a/app/src/main/java/fr/nitorac/aurionweb/security/SecurityHelperPostM.java b/app/src/main/java/fr/nitorac/aurionweb/security/SecurityHelperPostM.java new file mode 100644 index 0000000000000000000000000000000000000000..b15d398dd3ff759e35332a8a9658378c05f8f85d --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/security/SecurityHelperPostM.java @@ -0,0 +1,66 @@ +package fr.nitorac.aurionweb.security; + +import android.os.Build; +import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyProperties; +import android.util.Base64; + +import androidx.annotation.RequiresApi; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.Key; +import java.security.KeyStore; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.spec.GCMParameterSpec; + +@RequiresApi(api = Build.VERSION_CODES.M) +public class SecurityHelperPostM implements SecurityHelper { + + private static final String AndroidKeyStore = "AndroidKeyStore"; + private static final String AES_MODE = "AES/GCM/NoPadding"; + + private KeyStore keyStore; + + public SecurityHelperPostM() throws GeneralSecurityException, IOException { + init(); + } + + public void init() throws GeneralSecurityException, IOException { + keyStore = KeyStore.getInstance(AndroidKeyStore); + keyStore.load(null); + + if (!keyStore.containsAlias(SecurityManager.KEYSTORE_NAME)) { + KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, AndroidKeyStore); + keyGenerator.init( + new KeyGenParameterSpec.Builder(SecurityManager.KEYSTORE_NAME, + KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(KeyProperties.BLOCK_MODE_GCM) + .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) + .setRandomizedEncryptionRequired(true) + .build()); + keyGenerator.generateKey(); + } + } + + private Key getSecretKey() throws GeneralSecurityException { + return keyStore.getKey(SecurityManager.KEYSTORE_NAME, null); + } + + public String encrypt(String raw) throws GeneralSecurityException { + Cipher c = Cipher.getInstance(AES_MODE); + c.init(Cipher.ENCRYPT_MODE, getSecretKey(), c.getParameters()); + byte[] encodedBytes = c.doFinal(raw.getBytes(StandardCharsets.UTF_8)); + return Base64.encodeToString(encodedBytes, Base64.DEFAULT).replace("\n", "") + ":" + Base64.encodeToString(c.getIV(), Base64.DEFAULT); + } + + public String decrypt(String encrypted) throws GeneralSecurityException { + String[] data = encrypted.split(":"); + Cipher c = Cipher.getInstance(AES_MODE); + c.init(Cipher.DECRYPT_MODE, getSecretKey(), new GCMParameterSpec(128, Base64.decode(data[1], Base64.DEFAULT))); + return new String(c.doFinal(Base64.decode(data[0], Base64.DEFAULT)), StandardCharsets.UTF_8); + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/nitorac/aurionweb/security/SecurityHelperPreM.java b/app/src/main/java/fr/nitorac/aurionweb/security/SecurityHelperPreM.java new file mode 100644 index 0000000000000000000000000000000000000000..7a73b260ec3c49cf9f7ab0bd3be82d1dbecab10d --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/security/SecurityHelperPreM.java @@ -0,0 +1,135 @@ +package fr.nitorac.aurionweb.security; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.SharedPreferences; +import android.security.KeyPairGeneratorSpec; +import android.util.Base64; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Calendar; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.CipherOutputStream; +import javax.crypto.spec.SecretKeySpec; +import javax.security.auth.x500.X500Principal; + +public class SecurityHelperPreM implements SecurityHelper { + + private static final String AndroidKeyStore = "AndroidKeyStore"; + private static final String RSA_MODE = "RSA/ECB/PKCS1Padding"; + private static final String AES_MODE = "AES/ECB/PKCS7Padding"; + + private Context context; + private KeyStore keyStore; + + public SecurityHelperPreM(Context context) throws GeneralSecurityException, IOException { + this.context = context; + init(); + } + + public void init() throws GeneralSecurityException, IOException { + keyStore = KeyStore.getInstance(AndroidKeyStore); + keyStore.load(null);// Generate the RSA key pairs + if (!keyStore.containsAlias(SecurityManager.KEYSTORE_NAME)) { + // Generate a key pair for encryption + Calendar start = Calendar.getInstance(); + Calendar end = Calendar.getInstance(); + end.add(Calendar.YEAR, 60); + KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(context) + .setAlias(SecurityManager.KEYSTORE_NAME) + .setSubject(new X500Principal("CN=" + SecurityManager.KEYSTORE_NAME)) + .setSerialNumber(BigInteger.TEN) + .setStartDate(start.getTime()) + .setEndDate(end.getTime()) + .build(); + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", AndroidKeyStore); + kpg.initialize(spec); + kpg.generateKeyPair(); + } + + SharedPreferences pref = context.getSharedPreferences(SecurityManager.SEC_SHARED_PREF, Context.MODE_PRIVATE); + String enryptedKeyB64 = pref.getString(SecurityManager.ENCRYPT_KEY, null); + if (enryptedKeyB64 == null) { + byte[] key = new byte[16]; + SecureRandom secureRandom = new SecureRandom(); + secureRandom.nextBytes(key); + byte[] encryptedKey = rsaEncrypt(key); + enryptedKeyB64 = Base64.encodeToString(encryptedKey, Base64.DEFAULT); + SharedPreferences.Editor edit = pref.edit(); + edit.putString(SecurityManager.ENCRYPT_KEY, enryptedKeyB64); + edit.apply(); + } + } + + private byte[] rsaEncrypt(byte[] secret) throws GeneralSecurityException, IOException { + KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(SecurityManager.KEYSTORE_NAME, null); + // Encrypt the text + Cipher inputCipher = Cipher.getInstance(RSA_MODE, "AndroidOpenSSL"); + inputCipher.init(Cipher.ENCRYPT_MODE, privateKeyEntry.getCertificate().getPublicKey()); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, inputCipher); + cipherOutputStream.write(secret); + cipherOutputStream.close(); + + return outputStream.toByteArray(); + } + + private byte[] rsaDecrypt(byte[] encrypted) throws GeneralSecurityException, IOException { + KeyStore.PrivateKeyEntry privateKeyEntry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(SecurityManager.KEYSTORE_NAME, null); + Cipher output = Cipher.getInstance(RSA_MODE, "AndroidOpenSSL"); + output.init(Cipher.DECRYPT_MODE, privateKeyEntry.getPrivateKey()); + CipherInputStream cipherInputStream = new CipherInputStream( + new ByteArrayInputStream(encrypted), output); + ArrayList<Byte> values = new ArrayList<>(); + int nextByte; + while ((nextByte = cipherInputStream.read()) != -1) { + values.add((byte)nextByte); + } + + byte[] bytes = new byte[values.size()]; + for(int i = 0; i < bytes.length; i++) { + bytes[i] = values.get(i); + } + return bytes; + } + + private Key getSecretKey(Context context) throws GeneralSecurityException, IOException { + SharedPreferences pref = context.getSharedPreferences(SecurityManager.SEC_SHARED_PREF, Context.MODE_PRIVATE); + String enryptedKeyB64 = pref.getString(SecurityManager.ENCRYPT_KEY, null); + if(enryptedKeyB64 == null){ + throw new InvalidKeyException("Impossible de trouver la clé AES sauvegardée !"); + } + byte[] encryptedKey = Base64.decode(enryptedKeyB64, Base64.DEFAULT); + byte[] key = rsaDecrypt(encryptedKey); + return new SecretKeySpec(key, "AES"); + } + + @SuppressLint("GetInstance") // Vu qu'on fait du legacy ici, ça sert à rien d'avoir ces warnings + public String encrypt(String input) throws GeneralSecurityException, IOException { + Cipher c = Cipher.getInstance(AES_MODE, "BC"); + c.init(Cipher.ENCRYPT_MODE, getSecretKey(context)); + byte[] encodedBytes = c.doFinal(input.getBytes(StandardCharsets.UTF_8)); + return Base64.encodeToString(encodedBytes, Base64.DEFAULT); + } + + @SuppressLint("GetInstance") // Vu qu'on fait du legacy ici, ça sert à rien d'avoir ces warnings + public String decrypt(String encrypted) throws GeneralSecurityException, IOException { + Cipher c = Cipher.getInstance(AES_MODE, "BC"); + c.init(Cipher.DECRYPT_MODE, getSecretKey(context)); + return new String(c.doFinal(Base64.decode(encrypted, Base64.DEFAULT)), StandardCharsets.UTF_8); + } +} diff --git a/app/src/main/java/fr/nitorac/aurionweb/security/SecurityManager.java b/app/src/main/java/fr/nitorac/aurionweb/security/SecurityManager.java new file mode 100644 index 0000000000000000000000000000000000000000..0efccaeff166b17440f29211b1e96302e8202957 --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/security/SecurityManager.java @@ -0,0 +1,35 @@ +package fr.nitorac.aurionweb.security; + +import android.content.Context; +import android.os.Build; + +import java.io.IOException; +import java.security.GeneralSecurityException; + +/** + * On utilise une méthode assez compliquée pour stocker le mot de passe Aurionweb + * + * Etant donné qu'on ne peut utiliser une clé de chiffrement dans le code car trop facilement retrouvable, + * on la stocke en utilisant le chiffrement fourni par Android ! + * + * Cependant, le gestionnaire de chiffrement Android a changé d'API avant et après Android M, il faut donc gérer les deux possibilités + */ +public class SecurityManager { + + public static final String KEYSTORE_NAME = "NitAurionWeb"; + public static final String SEC_SHARED_PREF = "secstore"; + public static final String ENCRYPT_KEY = "aesKey"; + + private static SecurityHelper secHelp; + + public static SecurityHelper getSecurityHelper(Context c) throws GeneralSecurityException, IOException { + if(secHelp == null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + secHelp = new SecurityHelperPostM(); + }else{ + secHelp = new SecurityHelperPreM(c); + } + } + return secHelp; + } +} diff --git a/app/src/main/java/fr/nitorac/aurionweb/ui/dashboard/DashboardFragment.java b/app/src/main/java/fr/nitorac/aurionweb/ui/dashboard/DashboardFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..0ddc0559bea5248534b0a31cb3944d9aad42f29c --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/ui/dashboard/DashboardFragment.java @@ -0,0 +1,35 @@ +package fr.nitorac.aurionweb.ui.dashboard; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProviders; + +import fr.nitorac.aurionweb.R; + +public class DashboardFragment extends Fragment { + + private DashboardViewModel dashboardViewModel; + + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + dashboardViewModel = + ViewModelProviders.of(this).get(DashboardViewModel.class); + View root = inflater.inflate(R.layout.fragment_calendar, container, false); + final TextView textView = root.findViewById(R.id.text_dashboard); + dashboardViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() { + @Override + public void onChanged(@Nullable String s) { + textView.setText(s); + } + }); + return root; + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/nitorac/aurionweb/ui/dashboard/DashboardViewModel.java b/app/src/main/java/fr/nitorac/aurionweb/ui/dashboard/DashboardViewModel.java new file mode 100644 index 0000000000000000000000000000000000000000..84c5294ab9787ebaef4ae403c7c862a4772251f3 --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/ui/dashboard/DashboardViewModel.java @@ -0,0 +1,19 @@ +package fr.nitorac.aurionweb.ui.dashboard; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +public class DashboardViewModel extends ViewModel { + + private MutableLiveData<String> mText; + + public DashboardViewModel() { + mText = new MutableLiveData<>(); + mText.setValue("This is dashboard fragment"); + } + + public LiveData<String> getText() { + return mText; + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/nitorac/aurionweb/ui/home/HomeFragment.java b/app/src/main/java/fr/nitorac/aurionweb/ui/home/HomeFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..8f3370809543327c937e651f02b18b59d1863b34 --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/ui/home/HomeFragment.java @@ -0,0 +1,35 @@ +package fr.nitorac.aurionweb.ui.home; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProviders; + +import fr.nitorac.aurionweb.R; + +public class HomeFragment extends Fragment { + + private HomeViewModel homeViewModel; + + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + homeViewModel = + ViewModelProviders.of(this).get(HomeViewModel.class); + View root = inflater.inflate(R.layout.fragment_home, container, false); + final TextView textView = root.findViewById(R.id.text_home); + homeViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() { + @Override + public void onChanged(@Nullable String s) { + textView.setText(s); + } + }); + return root; + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/nitorac/aurionweb/ui/home/HomeViewModel.java b/app/src/main/java/fr/nitorac/aurionweb/ui/home/HomeViewModel.java new file mode 100644 index 0000000000000000000000000000000000000000..9f8c5f8c519e56342627654d7f6b359f0ee05849 --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/ui/home/HomeViewModel.java @@ -0,0 +1,19 @@ +package fr.nitorac.aurionweb.ui.home; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +public class HomeViewModel extends ViewModel { + + private MutableLiveData<String> mText; + + public HomeViewModel() { + mText = new MutableLiveData<>(); + mText.setValue("This is home fragment"); + } + + public LiveData<String> getText() { + return mText; + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/nitorac/aurionweb/ui/notifications/NotificationsFragment.java b/app/src/main/java/fr/nitorac/aurionweb/ui/notifications/NotificationsFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..f926c6db00442d5e5dc9ebaa111efc0bef49a205 --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/ui/notifications/NotificationsFragment.java @@ -0,0 +1,35 @@ +package fr.nitorac.aurionweb.ui.notifications; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProviders; + +import fr.nitorac.aurionweb.R; + +public class NotificationsFragment extends Fragment { + + private NotificationsViewModel notificationsViewModel; + + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + notificationsViewModel = + ViewModelProviders.of(this).get(NotificationsViewModel.class); + View root = inflater.inflate(R.layout.fragment_notifications, container, false); + final TextView textView = root.findViewById(R.id.text_notifications); + notificationsViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() { + @Override + public void onChanged(@Nullable String s) { + textView.setText(s); + } + }); + return root; + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/nitorac/aurionweb/ui/notifications/NotificationsViewModel.java b/app/src/main/java/fr/nitorac/aurionweb/ui/notifications/NotificationsViewModel.java new file mode 100644 index 0000000000000000000000000000000000000000..daea70f94234518b31b8f9ec69dce46ef76698c0 --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/ui/notifications/NotificationsViewModel.java @@ -0,0 +1,19 @@ +package fr.nitorac.aurionweb.ui.notifications; + +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; + +public class NotificationsViewModel extends ViewModel { + + private MutableLiveData<String> mText; + + public NotificationsViewModel() { + mText = new MutableLiveData<>(); + mText.setValue("This is notifications fragment"); + } + + public LiveData<String> getText() { + return mText; + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/nitorac/aurionweb/update/AsyncUpdateCheckerTask.java b/app/src/main/java/fr/nitorac/aurionweb/update/AsyncUpdateCheckerTask.java new file mode 100644 index 0000000000000000000000000000000000000000..cf295761d3520bc57268a68da0cb00f8183d7a9c --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/update/AsyncUpdateCheckerTask.java @@ -0,0 +1,60 @@ +package fr.nitorac.aurionweb.update; + +import android.content.Context; +import android.os.AsyncTask; +import android.widget.Toast; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.ref.WeakReference; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +import fr.nitorac.aurionweb.BuildConfig; + +public class AsyncUpdateCheckerTask extends AsyncTask<Void, Void, JsonObject> { + + private WeakReference<Context> weakContext; + + public AsyncUpdateCheckerTask(Context c){ + weakContext = new WeakReference<>(c); + } + + @Override + protected JsonObject doInBackground(Void... voids) { + try { + URL url = new URL(UpdateManager.CHECK_URL + URLEncoder.encode(BuildConfig.VERSION_NAME, StandardCharsets.UTF_8.toString())); + HttpURLConnection conn=(HttpURLConnection) url.openConnection(); + conn.setConnectTimeout(10000); // timing out in a minute + + JsonObject obj = new Gson().fromJson(new InputStreamReader(conn.getInputStream()), JsonObject.class); + if(!obj.get("version").getAsString().equalsIgnoreCase(BuildConfig.VERSION_NAME)){ + return obj; + }else{ + return new JsonObject(); + } + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + @Override + protected void onPostExecute(JsonObject obj) { + super.onPostExecute(obj); + if(weakContext == null){ + return; + } + if(obj == null){ + Toast.makeText(weakContext.get(), "Impossible de vérifier les mises à jour ...", Toast.LENGTH_SHORT).show(); + }else if(!obj.entrySet().isEmpty()){ + UpdateManager.updateAvailable = true; + UpdateManager.getInstance().spawnUpdateDialog(weakContext, obj); + } + } +} diff --git a/app/src/main/java/fr/nitorac/aurionweb/update/UpdateManager.java b/app/src/main/java/fr/nitorac/aurionweb/update/UpdateManager.java new file mode 100644 index 0000000000000000000000000000000000000000..abc3f46fee1809bfa0fca19cfbfe8ac9487b1bb6 --- /dev/null +++ b/app/src/main/java/fr/nitorac/aurionweb/update/UpdateManager.java @@ -0,0 +1,180 @@ +package fr.nitorac.aurionweb.update; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.PowerManager; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.core.content.ContextCompat; +import androidx.core.content.FileProvider; + +import com.afollestad.materialdialogs.MaterialDialog; +import com.afollestad.materialdialogs.customview.DialogCustomViewExtKt; +import com.downloader.Error; +import com.downloader.OnDownloadListener; +import com.downloader.PRDownloader; +import com.github.lzyzsd.circleprogress.CircleProgress; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import java.io.File; +import java.lang.ref.WeakReference; + +import fr.nitorac.aurionweb.BuildConfig; +import fr.nitorac.aurionweb.R; +import fr.nitorac.aurionweb.Utils; + +import static android.content.Context.POWER_SERVICE; + +public class UpdateManager { + public static String CHECK_URL = "https://ranini.iiens.net/aurionweb-check/update.php?version="; + + public static boolean updateAvailable = false; + public static int downloadId = -1; + private static UpdateManager instance; + + public UpdateManager(){ + + } + + public static void handleOnPause(){ + PRDownloader.pause(downloadId); + } + + public static void handleOnResume(){ + PRDownloader.resume(downloadId); + } + + public void checkUpdates(Context c){ + AsyncUpdateCheckerTask task = new AsyncUpdateCheckerTask(c); + task.execute(); + } + + public void startDownload(final WeakReference<Context> weakContext, String url){ + if(weakContext == null){ + return; + } + String tag = "UpdateDownload"; + Context c = weakContext.get(); + final MaterialDialog dialog = new MaterialDialog(c, MaterialDialog.getDEFAULT_BEHAVIOR()) + .title(null, "Téléchargement") + .icon(null, ContextCompat.getDrawable(c, R.drawable.ic_download)) + .cancelable(false); + DialogCustomViewExtKt.customView(dialog, R.layout.dialog_download, null, false, false, true, true); + final WeakReference<CircleProgress> circleProgress = new WeakReference<>(dialog.findViewById(R.id.circle_progress)); + final WeakReference<TextView> textProgress = new WeakReference<>(dialog.findViewById(R.id.progress_text)); + + PowerManager powerManager = (PowerManager) weakContext.get().getSystemService(POWER_SERVICE); + PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, + "AurionWeb::DownloadWakelock"); + + downloadId = PRDownloader.download(url, c.getCacheDir().getAbsolutePath(), "update.apk") + .setTag(tag) + .build() + .setOnStartOrResumeListener(() -> { + if(circleProgress != null){ + circleProgress.get().setProgress(0); + circleProgress.get().setMax(100); + } + + if(textProgress != null){ + textProgress.get().setText("Chargement ..."); + } + + if(!dialog.isShowing()){ + dialog.show(); + } + + if(!wakeLock.isHeld()){ + wakeLock.acquire(10*60*1000L /*30 minutes*/); + } + }) + .setOnPauseListener(() -> { + + }) + .setOnCancelListener(() -> { + if(weakContext != null){ + Toast.makeText(weakContext.get(), "Téléchargement annulé", Toast.LENGTH_LONG).show(); + } + wakeLock.release(); + }) + .setOnProgressListener(progress -> { + int frac = (int)((double)progress.currentBytes * 100.0 / (double)progress.totalBytes); + if(circleProgress != null){ + circleProgress.get().setProgress(frac); + } + if(textProgress != null){ + textProgress.get().setText(Utils.bytesToString(progress.currentBytes) + " / " + Utils.bytesToString(progress.totalBytes)); + } + }) + .start(new OnDownloadListener() { + @Override + public void onDownloadComplete() { + if(weakContext != null){ + Context context = weakContext.get(); + File file = new File(weakContext.get().getCacheDir().getAbsolutePath(), "update.apk"); + Uri apkURI = FileProvider.getUriForFile( + context, + context.getApplicationContext() + .getPackageName() + ".provider", file); + + Intent intent = new Intent(Intent.ACTION_VIEW, apkURI); + intent.setDataAndType(apkURI, "application/vnd.android.package-archive"); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true); + dialog.dismiss(); + context.getApplicationContext().startActivity(intent); + } + wakeLock.release(); + } + + @Override + public void onError(Error error) { + if(weakContext != null){ + error.getConnectionException().printStackTrace(); + Toast.makeText(weakContext.get(), "Erreur : " + error.getConnectionException().getMessage(), Toast.LENGTH_LONG).show(); + } + wakeLock.release(); + } + }); + } + + public void spawnUpdateDialog(WeakReference<Context> weakContext, JsonObject remote){ + if(weakContext == null){ + return; + } + Context c = weakContext.get(); + StringBuilder changelogs = new StringBuilder(); + + for(JsonElement el : remote.get("changelogs").getAsJsonArray()){ + JsonObject obj = el.getAsJsonObject(); + changelogs.append(obj.get("version").getAsString()).append(" :\n"); + for(JsonElement change : obj.get("msg").getAsJsonArray()){ + changelogs.append(" - ").append(change.getAsString()).append("\n"); + } + changelogs.append("\n"); + } + + MaterialDialog dialog = new MaterialDialog(c, MaterialDialog.getDEFAULT_BEHAVIOR()) + .title(R.string.dialog_update_title, null) + .icon(null, ContextCompat.getDrawable(c, R.drawable.ic_download)) + .message(null, String.format(c.getResources().getString(R.string.dialog_update_msg), BuildConfig.VERSION_NAME, remote.get("version").getAsString(), changelogs), null) + .cancelable(true) + .positiveButton(null, "METTRE A JOUR", materialDialog -> { + startDownload(weakContext, remote.get("url").getAsString()); + return null; + }) + .negativeButton(null, "ANNULER", materialDialog -> { + materialDialog.dismiss(); + return null; + }); + dialog.show(); + } + + public static UpdateManager getInstance(){ + return instance != null ? instance : (instance = new UpdateManager()); + } +} diff --git a/app/src/main/res/drawable-anydpi/ic_calendar.xml b/app/src/main/res/drawable-anydpi/ic_calendar.xml new file mode 100644 index 0000000000000000000000000000000000000000..4d9567073c344e3f8e6755defadd8b4dd126181c --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_calendar.xml @@ -0,0 +1,11 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="#FFFFFF" + android:alpha="0.8"> + <path + android:fillColor="@android:color/white" + android:pathData="M17,12h-5v5h5v-5zM16,1v2L8,3L8,1L6,1v2L5,3c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2h-1L18,1h-2zM19,19L5,19L5,8h14v11z"/> +</vector> diff --git a/app/src/main/res/drawable-anydpi/ic_download.xml b/app/src/main/res/drawable-anydpi/ic_download.xml new file mode 100644 index 0000000000000000000000000000000000000000..290ec39cf69315bb1b3e48ee9ccf283aee93b3c1 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_download.xml @@ -0,0 +1,16 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="#FFFFFF" + android:alpha="0.8"> + <group android:scaleX="0.90909094" + android:scaleY="0.90909094" + android:translateX="1.0909091" + android:translateY="1.0909091"> + <path + android:fillColor="@android:color/white" + android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z"/> + </group> +</vector> diff --git a/app/src/main/res/drawable-anydpi/ic_login.xml b/app/src/main/res/drawable-anydpi/ic_login.xml new file mode 100644 index 0000000000000000000000000000000000000000..1bc2080498920cce9743e2b1d5d289b81712ad8b --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_login.xml @@ -0,0 +1,11 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="#FFFFFF" + android:alpha="0.8"> + <path + android:fillColor="@android:color/white" + android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,5c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM12,19.2c-2.5,0 -4.71,-1.28 -6,-3.22 0.03,-1.99 4,-3.08 6,-3.08 1.99,0 5.97,1.09 6,3.08 -1.29,1.94 -3.5,3.22 -6,3.22z"/> +</vector> diff --git a/app/src/main/res/drawable-hdpi/ic_calendar.png b/app/src/main/res/drawable-hdpi/ic_calendar.png new file mode 100644 index 0000000000000000000000000000000000000000..45900b5204d22184d3621713898f19a24b0edc88 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_calendar.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_download.png b/app/src/main/res/drawable-hdpi/ic_download.png new file mode 100644 index 0000000000000000000000000000000000000000..2c614590ecfaf46600b184246b291a3c1d32692d Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_download.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_login.png b/app/src/main/res/drawable-hdpi/ic_login.png new file mode 100644 index 0000000000000000000000000000000000000000..abdfc916bac5477883bbd6539fe007edd16972c3 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_login.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_calendar.png b/app/src/main/res/drawable-mdpi/ic_calendar.png new file mode 100644 index 0000000000000000000000000000000000000000..8a4b66a35466df4297e88b716137b9941d08cab3 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_calendar.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_download.png b/app/src/main/res/drawable-mdpi/ic_download.png new file mode 100644 index 0000000000000000000000000000000000000000..94506fcc503df3ab86404a8ec0ca1fb43d58cb63 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_download.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_login.png b/app/src/main/res/drawable-mdpi/ic_login.png new file mode 100644 index 0000000000000000000000000000000000000000..2ec22cf526cec302eace27ed42996384024fd162 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_login.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_calendar.png b/app/src/main/res/drawable-xhdpi/ic_calendar.png new file mode 100644 index 0000000000000000000000000000000000000000..506e47b544918f67d3930ba134e610ce1f4bb176 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_calendar.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_download.png b/app/src/main/res/drawable-xhdpi/ic_download.png new file mode 100644 index 0000000000000000000000000000000000000000..3caed0d3d3ecbd57e4d3e1b255da73ea3fa8c724 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_download.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_login.png b/app/src/main/res/drawable-xhdpi/ic_login.png new file mode 100644 index 0000000000000000000000000000000000000000..469fcc0c804cf621497dce16d32b803a64d48fef Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_login.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_calendar.png b/app/src/main/res/drawable-xxhdpi/ic_calendar.png new file mode 100644 index 0000000000000000000000000000000000000000..57e81fd2d33ec8422aceaf72024b4d5fbc3beba1 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_calendar.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_download.png b/app/src/main/res/drawable-xxhdpi/ic_download.png new file mode 100644 index 0000000000000000000000000000000000000000..bd7b6b75f831af271a8b027b398a3f8870a53cb3 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_download.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_login.png b/app/src/main/res/drawable-xxhdpi/ic_login.png new file mode 100644 index 0000000000000000000000000000000000000000..635946f3249219620708901c70508fb2a2d660de Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_login.png differ diff --git a/app/src/main/res/drawable/ic_dashboard_black_24dp.xml b/app/src/main/res/drawable/ic_dashboard_black_24dp.xml new file mode 100644 index 0000000000000000000000000000000000000000..46fc8deec32bb06fedfe594815e568e3a6bf48b9 --- /dev/null +++ b/app/src/main/res/drawable/ic_dashboard_black_24dp.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M3,13h8L11,3L3,3v10zM3,21h8v-6L3,15v6zM13,21h8L21,11h-8v10zM13,3v6h8L21,3h-8z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_home_black_24dp.xml b/app/src/main/res/drawable/ic_home_black_24dp.xml new file mode 100644 index 0000000000000000000000000000000000000000..f8bb0b55633d3e41228e9e285149af5f1d839d08 --- /dev/null +++ b/app/src/main/res/drawable/ic_home_black_24dp.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z" /> +</vector> diff --git a/app/src/main/res/drawable/ic_notifications_black_24dp.xml b/app/src/main/res/drawable/ic_notifications_black_24dp.xml new file mode 100644 index 0000000000000000000000000000000000000000..78b75c39b55bc3cbbc18bfe7d9165fb3da7d03ff --- /dev/null +++ b/app/src/main/res/drawable/ic_notifications_black_24dp.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z" /> +</vector> diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000000000000000000000000000000000000..38c0aa8c56c73a05036c611a0197616887e2d4de --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingTop="?attr/actionBarSize"> + + <com.google.android.material.bottomnavigation.BottomNavigationView + android:id="@+id/nav_view" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="0dp" + android:layout_marginEnd="0dp" + android:background="?android:attr/windowBackground" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:menu="@menu/bottom_nav_menu" /> + + <fragment + android:id="@+id/nav_host_fragment" + android:name="androidx.navigation.fragment.NavHostFragment" + android:layout_width="match_parent" + android:layout_height="match_parent" + app:defaultNavHost="true" + app:layout_constraintBottom_toTopOf="@id/nav_view" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:navGraph="@navigation/mobile_navigation" /> + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_download.xml b/app/src/main/res/layout/dialog_download.xml new file mode 100644 index 0000000000000000000000000000000000000000..0feed7dd19277f69d39f2a0fefe14180491cf807 --- /dev/null +++ b/app/src/main/res/layout/dialog_download.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + xmlns:custom="http://schemas.android.com/apk/res-auto"> + + <com.github.lzyzsd.circleprogress.CircleProgress + android:id="@+id/circle_progress" + android:layout_width="100dp" + android:layout_height="100dp" + custom:circle_progress="0" + custom:layout_constraintBottom_toTopOf="@id/progress_text" + custom:layout_constraintEnd_toEndOf="parent" + custom:layout_constraintStart_toStartOf="parent" + custom:layout_constraintTop_toTopOf="parent" /> + <TextView + android:id="@+id/progress_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:layout_marginBottom="30dp" + custom:layout_constraintEnd_toEndOf="parent" + custom:layout_constraintStart_toStartOf="parent" + custom:layout_constraintTop_toBottomOf="@id/circle_progress" + custom:layout_constraintBottom_toBottomOf="parent" /> +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_loading.xml b/app/src/main/res/layout/dialog_loading.xml new file mode 100644 index 0000000000000000000000000000000000000000..305c462076498c58e49c7739b23eb8bc2ad725f6 --- /dev/null +++ b/app/src/main/res/layout/dialog_loading.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent"> + + <ProgressBar + android:id="@+id/progressBar" + style="?android:attr/progressBarStyle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_login.xml b/app/src/main/res/layout/dialog_login.xml new file mode 100644 index 0000000000000000000000000000000000000000..1d135e4f60a3475ed1fd1d19501fec16245796e0 --- /dev/null +++ b/app/src/main/res/layout/dialog_login.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent"> + + <TextView + android:id="@+id/loginLbl" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/login_login" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/loginInput" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:ems="60" + android:inputType="text" + android:hint="prenom.nom" + android:layout_marginTop="5dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/loginLbl" /> + + <TextView + android:id="@+id/passwordLbl" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/login_password" + android:layout_marginTop="30dp" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/loginInput" /> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/passwordLayout" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:passwordToggleEnabled="true" + android:layout_marginTop="5dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/passwordLbl"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/passwordInput" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="textWebPassword" + android:hint="••••••••••••"/> + + </com.google.android.material.textfield.TextInputLayout> + + <CheckBox + android:id="@+id/rememberMe" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="20dp" + android:layout_marginBottom="50dp" + android:text="Enregistrer les informations" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/passwordLayout" /> + + +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_calendar.xml b/app/src/main/res/layout/fragment_calendar.xml new file mode 100644 index 0000000000000000000000000000000000000000..166ab0e9e603c1f230a7b9514d293b963ab2309e --- /dev/null +++ b/app/src/main/res/layout/fragment_calendar.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".ui.dashboard.DashboardFragment"> + + <TextView + android:id="@+id/text_dashboard" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:textAlignment="center" + android:textSize="20sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml new file mode 100644 index 0000000000000000000000000000000000000000..f3d9b08ffe6101e25c77c5fae7e28bb5dfa11fbd --- /dev/null +++ b/app/src/main/res/layout/fragment_home.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".ui.home.HomeFragment"> + + <TextView + android:id="@+id/text_home" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:textAlignment="center" + android:textSize="20sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_notifications.xml b/app/src/main/res/layout/fragment_notifications.xml new file mode 100644 index 0000000000000000000000000000000000000000..d41793572bb3b8347ec4bced74b7bd4a43bed5d4 --- /dev/null +++ b/app/src/main/res/layout/fragment_notifications.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".ui.notifications.NotificationsFragment"> + + <TextView + android:id="@+id/text_notifications" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:textAlignment="center" + android:textSize="20sp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/menu/appbar_menu.xml b/app/src/main/res/menu/appbar_menu.xml new file mode 100644 index 0000000000000000000000000000000000000000..a219eba20b81da30855c9c342aac1480443167f2 --- /dev/null +++ b/app/src/main/res/menu/appbar_menu.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:id="@+id/connect_menuitem" + android:icon="@drawable/ic_login" + android:orderInCategory="0" + android:title="Connexion" + app:showAsAction="ifRoom|withText" /> +</menu> \ No newline at end of file diff --git a/app/src/main/res/menu/bottom_nav_menu.xml b/app/src/main/res/menu/bottom_nav_menu.xml new file mode 100644 index 0000000000000000000000000000000000000000..5485a387264f4737d0244d6064b7ed39ad81e5aa --- /dev/null +++ b/app/src/main/res/menu/bottom_nav_menu.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + + <item + android:id="@+id/navigation_home" + android:icon="@drawable/ic_home_black_24dp" + android:title="@string/title_home" /> + + <item + android:id="@+id/navigation_dashboard" + android:icon="@drawable/ic_calendar" + android:title="@string/title_calendar" /> + + <item + android:id="@+id/navigation_notifications" + android:icon="@drawable/ic_notifications_black_24dp" + android:title="@string/title_notifications" /> + +</menu> \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000000000000000000000000000000000..036d09bc5fd523323794379703c4a111d1e28a04 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@color/ic_launcher_background"/> + <foreground android:drawable="@mipmap/ic_launcher_foreground"/> +</adaptive-icon> \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000000000000000000000000000000000000..036d09bc5fd523323794379703c4a111d1e28a04 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@color/ic_launcher_background"/> + <foreground android:drawable="@mipmap/ic_launcher_foreground"/> +</adaptive-icon> \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..575265141fe1f63ffc50a45312df7965588609f2 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..28b0ed042b42ff8b5a196c2f7a1208e9ab84fcd9 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..575265141fe1f63ffc50a45312df7965588609f2 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..a8a8cd4b40affd65af7fc90bea46fb93cd064341 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..6b990032028b98b52ab3bb357ac747fc4e07e196 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..a8a8cd4b40affd65af7fc90bea46fb93cd064341 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..6a12762b797a846a88371c5d8766499736b977c9 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..46253eb6d25182180367e4433fb670029bb0fb58 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..6a12762b797a846a88371c5d8766499736b977c9 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..1919c7a7db0aedcd5d13eeb315daed5bb1c98b64 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..d28f2d811dcac0328512f9c2ea17a6e35339cc26 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..1919c7a7db0aedcd5d13eeb315daed5bb1c98b64 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..b07abd7593365d8ebec48b9116e2bcbe98020c35 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..f72fa44cd45a3ccf7cf2459fc5a51e37d1e1b1bf Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..b07abd7593365d8ebec48b9116e2bcbe98020c35 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml new file mode 100644 index 0000000000000000000000000000000000000000..aadbe802166818243e2501bc132466071180e66b --- /dev/null +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<navigation 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:id="@+id/mobile_navigation" + app:startDestination="@+id/navigation_home"> + + <fragment + android:id="@+id/navigation_home" + android:name="fr.nitorac.aurionweb.ui.home.HomeFragment" + android:label="@string/title_home" + tools:layout="@layout/fragment_home" /> + + <fragment + android:id="@+id/navigation_dashboard" + android:name="fr.nitorac.aurionweb.ui.dashboard.DashboardFragment" + android:label="@string/title_calendar" + tools:layout="@layout/fragment_calendar" /> + + <fragment + android:id="@+id/navigation_notifications" + android:name="fr.nitorac.aurionweb.ui.notifications.NotificationsFragment" + android:label="@string/title_notifications" + tools:layout="@layout/fragment_notifications" /> +</navigation> \ No newline at end of file diff --git a/app/src/main/res/raw/aurionweb_ensiie_fr.pem b/app/src/main/res/raw/aurionweb_ensiie_fr.pem new file mode 100644 index 0000000000000000000000000000000000000000..4b1c5b4a412d6e7a8841bb6e09750dc288f05482 --- /dev/null +++ b/app/src/main/res/raw/aurionweb_ensiie_fr.pem @@ -0,0 +1,42 @@ +-----BEGIN CERTIFICATE----- +MIIHTzCCBjegAwIBAgIQCQtdJVL7YaPr+9+02mt9CzANBgkqhkiG9w0BAQsFADBk +MQswCQYDVQQGEwJOTDEWMBQGA1UECBMNTm9vcmQtSG9sbGFuZDESMBAGA1UEBxMJ +QW1zdGVyZGFtMQ8wDQYDVQQKEwZURVJFTkExGDAWBgNVBAMTD1RFUkVOQSBTU0wg +Q0EgMzAeFw0xODA3MDQwMDAwMDBaFw0yMDEwMDYwMDAwMDBaMIGYMQswCQYDVQQG +EwJGUjETMBEGA1UEBxMKRVZSWSBDRURFWDFIMEYGA1UECgw/RWNvbGUgTmF0aW9u +YWxlIFN1cMOpcmlldXJlIGQnaW5mby4gcG91ciBsJ0luZC4gZXQgbCdFbnRyZXBy +aXNlMQwwCgYDVQQLEwNDUkkxHDAaBgNVBAMTE2F1cmlvbndlYi5lbnNpaWUuZnIw +ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCnNDksvsGjgcM3ThkXVAx5 +xxJXoDUYbnuqdqyi0oA1uSjHVcC+4UgA2lw+ZY/FeCG7weAPejGZqqZhY6vEq54M +hysnNZH3VDgFUeixq1D8gEjOxkdOwXm88utvplan3PgcOIckt2BjsEbfxseX1v4u +4VDH5/99MeTBeAjxrknZPjWdILiPMx/9BgKKOHbfXhrKZoeZ6bfsEoVzKWjlAqPP +rxBYbAeX4kFZE0VWLrlqs2VNzKfsmvfm+GAKKf8h0iRlUM1lG8bBsmfxb0pjg9Tm +mVJgr2+8R89V1j59hFiBHjfyqYoSJNirUlhXuGFKeGOBzJm4jDReWHPe5BBthnk3 +AgMBAAGjggPGMIIDwjAfBgNVHSMEGDAWgBRn/YggFCeYxwnSJRm76VERY3VQYjAd +BgNVHQ4EFgQU9RX+8yvm3V3rbDKQz3s6FJegGqAwHgYDVR0RBBcwFYITYXVyaW9u +d2ViLmVuc2lpZS5mcjAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUH +AwEGCCsGAQUFBwMCMGsGA1UdHwRkMGIwL6AtoCuGKWh0dHA6Ly9jcmwzLmRpZ2lj +ZXJ0LmNvbS9URVJFTkFTU0xDQTMuY3JsMC+gLaArhilodHRwOi8vY3JsNC5kaWdp +Y2VydC5jb20vVEVSRU5BU1NMQ0EzLmNybDBMBgNVHSAERTBDMDcGCWCGSAGG/WwB +ATAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdpY2VydC5jb20vQ1BTMAgG +BmeBDAECAjBuBggrBgEFBQcBAQRiMGAwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3Nw +LmRpZ2ljZXJ0LmNvbTA4BggrBgEFBQcwAoYsaHR0cDovL2NhY2VydHMuZGlnaWNl +cnQuY29tL1RFUkVOQVNTTENBMy5jcnQwDAYDVR0TAQH/BAIwADCCAfYGCisGAQQB +1nkCBAIEggHmBIIB4gHgAHYApLkJkLQYWBSHuxOizGdwCjw1mAT5G9+443fNDsgN +3BAAAAFkZD6SfgAABAMARzBFAiEAhqeRjwyl7IWrb46mS4n+I31HxLNhDDLn9jEi +VN3Cip0CIDqnppqzhC6e3RHYFTXrvkdQjrzAw/LRtgJ+M9J2idNPAHYAh3W/51l8 ++IxDmV+9827/Vo1HVjb/SrVgwbTq/16ggw8AAAFkZD6TUwAABAMARzBFAiEA/CFX +dd1HUPCDZjMz+oqicI6H7UCUKTO1Y9ojCJwUFzECIGOU4F7s2gyV7j8rm/xF3Vw/ +jeP1nDWDpO/bs+6JO6XtAHcA7ku9t3XOYLrhQmkfq+GeZqMPfl+wctiDAMR7iXqo +/csAAAFkZD6SwQAABAMASDBGAiEAuWbkrYVras5GeloBf4224WOv+ZRg+QV3xQGn +MmVTFtICIQCz73qOSo/HyT1Ctak7NWYwHlW/bRIu61Ol+U7LxZr01wB1ALvZ37wf +inG1k5Qjl6qSe0c4V5UKq1LoGpCWZDaOHtGFAAABZGQ+k3MAAAQDAEYwRAIgcZDt +pPgy3Jd100lSKUGqIPZ9okT8S2Q3umjmM+52DPICIBeX+iCxSIiIF3wQAo+B22Qo +GUzYVMEqpV5jN2iI4Y5VMA0GCSqGSIb3DQEBCwUAA4IBAQASfy7UQIOWiJhSSxJj +xwxhe1uyVn/+JNbaqNyDGGz0uL8hGZTt6SUGrjV+/RMnL0l8d5eApe73Chr5VcYn +3kcuY4B3EKzMEonuGuMOJmPzk65YWk015lHJc0t+GOMi0BN1Ven/DweDRlxIb18w +CM/Tvua27q0PBGPDjQ1m+QRZ9gaEU1XyqD7Cts4SFfjwxZL4EjAz/dQFsGfB4a4Z +l3l94e7vGZfMUOoORFkrV55Qm5zqodQyRv6A46M1v6+kfVMUPyppVCM2F54EPw6M +7r6RULICwobirLXdT4CajKB4bOIjm0e09zLzuij4TLAo34mOATG9DYV2HOFRsCem +NJBE +-----END CERTIFICATE----- \ No newline at end of file diff --git a/app/src/main/res/raw/cas_ensiie_fr.pem b/app/src/main/res/raw/cas_ensiie_fr.pem new file mode 100644 index 0000000000000000000000000000000000000000..de4efbe1faf01b92c9c4cfe23c5ce8d00d4bffb6 --- /dev/null +++ b/app/src/main/res/raw/cas_ensiie_fr.pem @@ -0,0 +1,41 @@ +-----BEGIN CERTIFICATE----- +MIIHPTCCBiWgAwIBAgIQBWafOeFdJ+iHekwJBoSBCDANBgkqhkiG9w0BAQsFADBk +MQswCQYDVQQGEwJOTDEWMBQGA1UECBMNTm9vcmQtSG9sbGFuZDESMBAGA1UEBxMJ +QW1zdGVyZGFtMQ8wDQYDVQQKEwZURVJFTkExGDAWBgNVBAMTD1RFUkVOQSBTU0wg +Q0EgMzAeFw0yMDA0MDcwMDAwMDBaFw0yMjA3MTEwMDAwMDBaMIGNMQswCQYDVQQG +EwJGUjENMAsGA1UEBxMERVZSWTFIMEYGA1UECgw/RWNvbGUgTmF0aW9uYWxlIFN1 +cMOpcmlldXJlIGQnaW5mby4gcG91ciBsJ0luZC4gZXQgbCdFbnRyZXByaXNlMQ0w +CwYDVQQLEwRESVNJMRYwFAYDVQQDEw1jYXMuZW5zaWllLmZyMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEApH5oCBw+lp1LzM5gvsWMXB2rdTUOcyD1elfk +lOAUvl5tkQxHhnSOQo6Ywnf6vKP7GF5yqT6sCqDZxHxITXY1YgHlkageW90Rvlga +OkswpHT6l0wN0msQVFpUZAV94LY2LqgPzcDgdCqS9fW17S6jNlNezlCVCS4eYFos +og//D3LU4WWOlFIbeq0BiEyR2SsSvgrtm3IVA9At5EoXL5f8nxGJetCz9K0Okm/P +R/EK7zIEU/UsmLlUSn8mBbgd63/jBRvzvKYPZyLFhPgdLiEyLRADMLGfoKFU4IND +aj5WiN9CnjePzbrViON7rXAoFGa6WmsIjP3FPqg/9VHti2DrdwIDAQABo4IDvzCC +A7swHwYDVR0jBBgwFoAUZ/2IIBQnmMcJ0iUZu+lREWN1UGIwHQYDVR0OBBYEFLTb +kMQmJI6GUM30mxKHpH0GABEYMBgGA1UdEQQRMA+CDWNhcy5lbnNpaWUuZnIwDgYD +VR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBrBgNV +HR8EZDBiMC+gLaArhilodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vVEVSRU5BU1NM +Q0EzLmNybDAvoC2gK4YpaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL1RFUkVOQVNT +TENBMy5jcmwwTAYDVR0gBEUwQzA3BglghkgBhv1sAQEwKjAoBggrBgEFBQcCARYc +aHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAIBgZngQwBAgIwbgYIKwYBBQUH +AQEEYjBgMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wOAYI +KwYBBQUHMAKGLGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9URVJFTkFTU0xD +QTMuY3J0MAwGA1UdEwEB/wQCMAAwggH1BgorBgEEAdZ5AgQCBIIB5QSCAeEB3wB2 +AKS5CZC0GFgUh7sTosxncAo8NZgE+RvfuON3zQ7IDdwQAAABcVPmeiEAAAQDAEcw +RQIgGnTSe4WZ+XAc94FW3+X33bHh4545LfhedHfqve1b5ncCIQDX5U6yy0UPHH/v +sF63PRbAUQVYTwHtgjMTD+myHdxI4wB1ACJFRQdZVSRWlj+hL/H3bYbgIyZjrcBL +f13Gg1xu4g8CAAABcVPmek4AAAQDAEYwRAIgcwGGmxrOD1rnHTNR4RJBcZWuoG2U +h49ZVhsw+egrqm0CIDit+elupvyTGMggdjY8M5O4H3PX7eflWkX00B93Z67EAHcA +UaOw9f0BeZxWbbg3eI8MpHrMGyfL956IQpoN/tSLBeUAAAFxU+Z6kQAABAMASDBG +AiEA7MqBM8nCli74ADm2yZNkQNBG5maK7mz7Nt/cbSxkuGQCIQCIeCd4y+EUnoSD +7aksYyn0qOn7SY5n4AZypyjG7UDjMgB1AEHIyrHfIkZKEMahOglCh15OMYsbA+vr +S8do8JBilgb2AAABcVPmef8AAAQDAEYwRAIgSYstHo0iQh3A5/xtP7/pU/GvMuDK +FEYMiEk5AG8pJzACIDp/cFGRQoeVbbzdGR+dYzhkVMmSoOpuqCCwZZNlm3hIMA0G +CSqGSIb3DQEBCwUAA4IBAQAIGGzSixN4XaiJvG5MAW7Awo5FaXXf4jbKABuQ2x/a +6XpOy1eh9uqYYF6MHJqdLo63KtO+aKowS7OLQHtlDw/Zgtr1mnBt6xLA8BiFzy2+ +QFiA4d46iB1dkGecbnMNftXt99WQQFC72VO4hlmZDYUcjFcjRfqceDUojgYMtq4R +6npY8Sb0IPFWWPhWYYwIdboeRmEurKgz2nVDfFaEmQnJzKHfMclIXUSXGqgR7lV2 +FnSEJFJ3x1ef/gT4S/dIA72/mxUT8tovul4RS9pCXSOBe6VlJa4WcwkM7IrBwjsJ +j51+5I7zk8RjE+JOxXJFfM/bUGJ1vmSW8vdXiMTybVIB +-----END CERTIFICATE----- \ No newline at end of file diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..86ad9fe4787a22eebcd957e46c59e806f492a76d --- /dev/null +++ b/app/src/main/res/values-fr-rFR/strings.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <string name="app_name">AurionWeb</string> + <string name="title_home">Accueil</string> + <string name="dialog_update_title">Mise à jour</string> + <string name="dialog_update_msg">Une mise à jour est disponible !\n(%1$s -> %2$s)\n\nChangements :\n%3$s\n\nVoulez-vous mettre à jour ?</string> + <string name="login_login">Nom d\'utilisateur</string> + <string name="login_password">Mot de passe</string> + <string name="title_calendar">Emploi du temps</string> + <string name="loading">Chargement ...</string> +</resources> \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000000000000000000000000000000000000..fd454c4279ee3361b6c83c1c4d2775c3a9fd5ee6 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="colorPrimary">#42A5F5</color> + <color name="colorPrimaryDark">#3F51B5</color> + <color name="colorAccent">#F44336</color> +</resources> \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000000000000000000000000000000000000..e00c2dd143c595389b3cf8a32d9dc6aff48ec367 --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ +<resources> + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> +</resources> \ No newline at end of file diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000000000000000000000000000000000000..35a7725424991e727b5deed53440fd837cc2584f --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="ic_launcher_background">#3D2E27</color> +</resources> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000000000000000000000000000000000..6dd03453f84cb8545fa58bd7c0ad8ef7ef76b8cf --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,11 @@ +<resources> + <string name="app_name">AurionWeb</string> + <string name="title_home">Home</string> + <string name="title_calendar">Calendar</string> + <string name="title_notifications">Notifications</string> + <string name="dialog_update_title">Update</string> + <string name="dialog_update_msg">An update is available !\n(%1$s -> %2$s)\n\nChangelogs:\n%3$s\n\nDo you want to update ?</string> + <string name="login_login">Login</string> + <string name="login_password">Password</string> + <string name="loading">Loading...</string> +</resources> \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000000000000000000000000000000000000..f4cf47d48fee4ff8cbb75a75cb9e4c8ed2fe755f --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ +<resources> + <!-- Base application theme. --> + <style name="AppTheme" parent="Theme.AppCompat.DayNight"> + <!-- Customize your theme here. --> + <item name="colorPrimary">@color/colorPrimary</item> + <item name="colorPrimaryDark">@color/colorPrimaryDark</item> + <item name="colorAccent">@color/colorAccent</item> + <item name="md_corner_radius">16dp</item> + </style> + +</resources> \ No newline at end of file diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml new file mode 100644 index 0000000000000000000000000000000000000000..20c6b33c627d7eecfec34e2ea98d1d1e7c5e1bc8 --- /dev/null +++ b/app/src/main/res/xml/provider_paths.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<paths> + <files-path name="internal_data" path="."/> + <cache-path name="cache_data" path="."/> +</paths> \ No newline at end of file diff --git a/app/src/test/java/fr/nitorac/aurionweb/ExampleUnitTest.java b/app/src/test/java/fr/nitorac/aurionweb/ExampleUnitTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b982752841ff13d927e270d96fc36767e625b617 --- /dev/null +++ b/app/src/test/java/fr/nitorac/aurionweb/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package fr.nitorac.aurionweb; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see <a href="http://d.android.com/tools/testing">Testing documentation</a> + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..bfb75dc907684dc261a7b8f6de4f6996df7dffd6 --- /dev/null +++ b/build.gradle @@ -0,0 +1,27 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + ext.kotlin_version = '1.4.10' + repositories { + google() + jcenter() + } + dependencies { + classpath "com.android.tools.build:gradle:4.0.1" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + jcenter() + maven { url 'https://jitpack.io' } + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000000000000000000000000000000000000..c52ac9b79716c6c430b6ad840ffbafc9c3da528a --- /dev/null +++ b/gradle.properties @@ -0,0 +1,19 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000000000000000000000000000000000..919be2fe32fb04fb62bf796c7a98a3825419c2dd --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Oct 02 17:31:26 CEST 2020 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip diff --git a/gradlew b/gradlew new file mode 100644 index 0000000000000000000000000000000000000000..cccdd3d517fc5249beaefa600691cf150f2fa3e6 --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000000000000000000000000000000000..f9553162f122c71b34635112e717c3e733b5b212 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/local.properties b/local.properties new file mode 100644 index 0000000000000000000000000000000000000000..ee4e8c4d1bbad491982b23a45f92b48a9a186cd8 --- /dev/null +++ b/local.properties @@ -0,0 +1,10 @@ +## This file is automatically generated by Android Studio. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file should *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +sdk.dir=D\:\\Users\\Nitorac\\AppData\\Local\\sdk \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000000000000000000000000000000000000..a8df4e466a60a93401b0ced0bed7b21c11dadeda --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +include ':app' +rootProject.name = "AurionWeb" \ No newline at end of file