├── .gitignore
├── android
├── app
│ ├── .gitignore
│ ├── src
│ │ ├── main
│ │ │ ├── res
│ │ │ │ ├── values
│ │ │ │ │ ├── strings.xml
│ │ │ │ │ ├── dimens.xml
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ └── styles.xml
│ │ │ │ ├── drawable
│ │ │ │ │ ├── bubbles.jpg
│ │ │ │ │ ├── ic_file_download_green_24dp.xml
│ │ │ │ │ ├── ic_play_store.xml
│ │ │ │ │ └── ic_play_store_black.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ ├── ic_launcher.png
│ │ │ │ │ └── ic_launcher_round.png
│ │ │ │ └── layout
│ │ │ │ │ ├── activity_main.xml
│ │ │ │ │ ├── app_header_cell.xml
│ │ │ │ │ ├── appversion_header_cell.xml
│ │ │ │ │ ├── appversions_list.xml
│ │ │ │ │ ├── apps_list.xml
│ │ │ │ │ ├── appversion_header.xml
│ │ │ │ │ ├── appversion_cell.xml
│ │ │ │ │ └── app_cell.xml
│ │ │ ├── java
│ │ │ │ └── florent37
│ │ │ │ │ └── github
│ │ │ │ │ └── com
│ │ │ │ │ └── mam
│ │ │ │ │ ├── repository
│ │ │ │ │ ├── Repository.java
│ │ │ │ │ └── AppRepository.java
│ │ │ │ │ ├── model
│ │ │ │ │ ├── AppResponse.java
│ │ │ │ │ ├── AppsResponse.java
│ │ │ │ │ ├── AppVersion.java
│ │ │ │ │ └── App.java
│ │ │ │ │ ├── common
│ │ │ │ │ ├── BaseFragment.java
│ │ │ │ │ ├── BaseActivity.java
│ │ │ │ │ ├── ClickListenerWrapper.java
│ │ │ │ │ └── AbstractPresenter.java
│ │ │ │ │ ├── api
│ │ │ │ │ └── MamAPI.java
│ │ │ │ │ ├── ui
│ │ │ │ │ ├── apps
│ │ │ │ │ │ ├── AppsActivity.java
│ │ │ │ │ │ ├── AppsHeaderViewHolder.java
│ │ │ │ │ │ ├── AppsPresenter.java
│ │ │ │ │ │ ├── AppsAdapter.java
│ │ │ │ │ │ ├── AppsListFragment.java
│ │ │ │ │ │ └── AppViewHolder.java
│ │ │ │ │ └── versions
│ │ │ │ │ │ ├── VersionsActivity.java
│ │ │ │ │ │ ├── VersionsHeaderViewHolder.java
│ │ │ │ │ │ ├── VersionsPresenter.java
│ │ │ │ │ │ ├── VersionsViewHolder.java
│ │ │ │ │ │ ├── VersionsAdapter.java
│ │ │ │ │ │ └── VersionsListFragment.java
│ │ │ │ │ ├── bus
│ │ │ │ │ └── MainBus.java
│ │ │ │ │ ├── MainApplication.java
│ │ │ │ │ └── dagger
│ │ │ │ │ ├── AppComponent.java
│ │ │ │ │ └── AppModule.java
│ │ │ └── AndroidManifest.xml
│ │ ├── test
│ │ │ └── java
│ │ │ │ └── florent37
│ │ │ │ └── github
│ │ │ │ └── com
│ │ │ │ └── mam
│ │ │ │ └── ExampleUnitTest.java
│ │ ├── androidTest
│ │ │ └── java
│ │ │ │ └── florent37
│ │ │ │ └── github
│ │ │ │ └── com
│ │ │ │ └── mam
│ │ │ │ └── ExampleInstrumentedTest.java
│ │ ├── prod
│ │ │ └── java
│ │ │ │ └── mam
│ │ │ │ └── repository
│ │ │ │ └── AppRepositoryImpl.java
│ │ └── mock
│ │ │ └── java
│ │ │ └── mam
│ │ │ └── repository
│ │ │ └── AppRepositoryImpl.java
│ ├── proguard-rules.pro
│ ├── upload.gradle
│ └── build.gradle
├── settings.gradle
├── .idea
│ ├── copyright
│ │ └── profiles_settings.xml
│ ├── markdown-navigator
│ │ └── profiles_settings.xml
│ ├── modules.xml
│ ├── runConfigurations.xml
│ ├── compiler.xml
│ └── misc.xml
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradle.properties
├── build.gradle
├── .gitignore
├── gradlew.bat
└── gradlew
├── website
├── .gitignore
├── start-api.sh
├── php
│ ├── config.json
│ ├── controller
│ │ ├── AppController.php
│ │ └── AppControllerImpl.php
│ ├── model
│ │ ├── AppsResponse.php
│ │ ├── AppVersionResponse.php
│ │ ├── AppVersion.php
│ │ └── App.php
│ ├── filesaver
│ │ ├── FileSaver.php
│ │ └── FileSaverDirectory.php
│ ├── index.php
│ ├── DependencyManager.php
│ ├── repository
│ │ ├── AppRepository.php
│ │ └── AppRepositoryDb.php
│ ├── utils.php
│ ├── api.php
│ └── database
│ │ ├── db.php
│ │ └── APPLICATION.sql
└── docker
│ ├── docker-compose.yml
│ ├── compose.yml
│ └── site
│ └── Dockerfile
├── README.md
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/android/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/website/.gitignore:
--------------------------------------------------------------------------------
1 | #website gitignore
2 |
--------------------------------------------------------------------------------
/website/start-api.sh:
--------------------------------------------------------------------------------
1 | cd docker
2 | docker-compose up -d
3 |
--------------------------------------------------------------------------------
/website/php/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "datasource" : "db", //file ?
3 | "filesaver" : "directory" //git, ftp ?
4 | }
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | MAM
3 |
4 |
--------------------------------------------------------------------------------
/android/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/florent37/Open-Mam/HEAD/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/bubbles.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/florent37/Open-Mam/HEAD/android/app/src/main/res/drawable/bubbles.jpg
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/florent37/Open-Mam/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/florent37/Open-Mam/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/florent37/Open-Mam/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/florent37/Open-Mam/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/.idea/markdown-navigator/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/florent37/Open-Mam/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/florent37/Open-Mam/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/florent37/Open-Mam/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/florent37/Open-Mam/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/florent37/Open-Mam/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/florent37/Open-Mam/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 200dp
4 | 115dp
5 |
--------------------------------------------------------------------------------
/website/php/controller/AppController.php:
--------------------------------------------------------------------------------
1 |
2 |
3 | @color/md_green_500
4 | @color/md_green_800
5 | #FF4081
6 | @color/md_grey_300
7 |
8 |
--------------------------------------------------------------------------------
/website/php/filesaver/FileSaverDirectory.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/website/php/index.php:
--------------------------------------------------------------------------------
1 | open($fileName);
11 | if ($res === TRUE) {
12 | $zip->extractTo($output);
13 | $zip->close();
14 | echo 'woot!';
15 | } else {
16 | echo $res;
17 | }
18 | */
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/website/php/DependencyManager.php:
--------------------------------------------------------------------------------
1 | versions;
12 |
13 | public List getVersions() {
14 | return versions;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/app_header_cell.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/website/php/repository/AppRepository.php:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/ic_file_download_green_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/model/AppsResponse.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.model;
2 |
3 | import java.util.List;
4 |
5 | /**
6 | * Created by florentchampigny on 18/06/2017.
7 | */
8 |
9 | public class AppsResponse {
10 | private List apps;
11 |
12 | public List getApps() {
13 | return apps;
14 | }
15 |
16 | public void setApps(List apps) {
17 | this.apps = apps;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/repository/AppRepository.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.repository;
2 |
3 | import java.util.List;
4 |
5 | import florent37.github.com.mam.model.App;
6 | import florent37.github.com.mam.model.AppVersion;
7 | import io.reactivex.Single;
8 |
9 | /**
10 | * Created by florentchampigny on 18/06/2017.
11 | */
12 |
13 | public interface AppRepository {
14 | Single> applications();
15 | Single> application(String name);
16 | }
17 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/test/java/florent37/github/com/mam/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/website/php/utils.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/common/BaseFragment.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.common;
2 |
3 | import android.arch.lifecycle.LifecycleRegistry;
4 | import android.arch.lifecycle.LifecycleRegistryOwner;
5 | import android.support.v4.app.Fragment;
6 |
7 | /**
8 | * Created by florentchampigny on 20/06/2017.
9 | */
10 |
11 | public class BaseFragment extends Fragment implements LifecycleRegistryOwner {
12 |
13 | private final LifecycleRegistry mRegistry = new LifecycleRegistry(this);
14 |
15 | @Override
16 | public LifecycleRegistry getLifecycle() {
17 | return mRegistry;
18 | }
19 | }
--------------------------------------------------------------------------------
/website/docker/compose.yml:
--------------------------------------------------------------------------------
1 | site:
2 | build: site
3 | ports :
4 | - "80:80"
5 | volumes:
6 | - /Users/florentchampigny/dev/mysite/www:/var/www/html/
7 | links:
8 | - database
9 | phpmyadmin:
10 | image: corbinu/docker-phpmyadmin
11 | ports :
12 | - "8080:80"
13 | environment:
14 | - MYSQL_USERNAME=root
15 | - MYSQL_PASSWORD=password
16 | links:
17 | - database:mysql
18 | database:
19 | image: mysql:5.5
20 | ports:
21 | - "3306:3306"
22 | environment:
23 | - MYSQL_ROOT_PASSWORD=password
24 | - MYSQL_DATABASE=mysite
25 | - MYSQL_USER=mysite
26 | - MYSQL_PASSWORD=password
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/api/MamAPI.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.api;
2 |
3 | import florent37.github.com.mam.model.AppResponse;
4 | import florent37.github.com.mam.model.AppsResponse;
5 | import io.reactivex.Single;
6 | import retrofit2.http.GET;
7 | import retrofit2.http.Query;
8 |
9 | /**
10 | * Created by florentchampigny on 18/06/2017.
11 | */
12 |
13 | public interface MamAPI {
14 |
15 | @GET("api.php?method=apps&platform=android")
16 | Single apps();
17 |
18 | @GET("api.php?method=app&platform=android")
19 | Single app(@Query("name") String name);
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/appversions_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/common/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.common;
2 |
3 | import android.arch.lifecycle.LifecycleRegistry;
4 | import android.arch.lifecycle.LifecycleRegistryOwner;
5 | import android.support.v7.app.AppCompatActivity;
6 |
7 | /**
8 | * Created by florentchampigny on 20/06/2017.
9 | */
10 |
11 | public class BaseActivity extends AppCompatActivity implements LifecycleRegistryOwner {
12 |
13 | private final LifecycleRegistry mRegistry = new LifecycleRegistry(this);
14 |
15 | @Override
16 | public LifecycleRegistry getLifecycle() {
17 | return mRegistry;
18 | }
19 | }
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/common/ClickListenerWrapper.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.common;
2 |
3 | /**
4 | * Created by florentchampigny on 20/06/2017.
5 | */
6 |
7 | public class ClickListenerWrapper {
8 |
9 | public V listener;
10 |
11 | public ClickListenerWrapper() {
12 | }
13 |
14 | public void setListener(V listener) {
15 | this.listener = listener;
16 | }
17 |
18 | public interface Callback{
19 | void get(V listener);
20 | }
21 |
22 | public void getListener(Callback callback){
23 | if (listener != null) {
24 | callback.get(listener);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/website/docker/site/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:5.6-apache
2 |
3 | RUN apt-get update && apt-get install -y zlib1g-dev
4 |
5 | # Install PDO MySQL driver
6 | # See https://github.com/docker-library/php/issues/62
7 | RUN docker-php-ext-install pdo mysql
8 | RUN docker-php-ext-install pdo mysqli
9 | RUN docker-php-ext-install pdo pdo_mysql
10 | RUN docker-php-ext-install mysql mysqli
11 | RUN docker-php-ext-install zip
12 |
13 | #RUN echo "file_uploads = On" > /usr/local/etc/php/conf.d/file_upload.ini
14 |
15 | # Workaround for write permission on write to MacOS X volumes
16 | # See https://github.com/boot2docker/boot2docker/pull/534
17 | RUN usermod -u 1000 www-data
18 |
19 | # Enable Apache mod_rewrite
20 | RUN a2enmod rewrite
--------------------------------------------------------------------------------
/website/php/api.php:
--------------------------------------------------------------------------------
1 | start($_GET);
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/ui/apps/AppsActivity.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.ui.apps;
2 |
3 | import android.os.Bundle;
4 |
5 | import florent37.github.com.mam.R;
6 | import florent37.github.com.mam.common.BaseActivity;
7 |
8 | public class AppsActivity extends BaseActivity {
9 |
10 | @Override
11 | protected void onCreate(Bundle savedInstanceState) {
12 | super.onCreate(savedInstanceState);
13 | setContentView(R.layout.activity_main);
14 | displayApps();
15 | }
16 |
17 | public void displayApps() {
18 | getSupportFragmentManager().beginTransaction()
19 | .replace(R.id.content, AppsListFragment.newInstance())
20 | .commitAllowingStateLoss();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/android/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.3.3'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | maven {
19 | url "https://maven.google.com"
20 | }
21 | maven {
22 | url "http://dl.bintray.com/florent37/maven"
23 | }
24 | maven { url 'http://github.com/wada811/Android-Material-Design-Colors/raw/master/repository/' }
25 | }
26 | }
27 |
28 | task clean(type: Delete) {
29 | delete rootProject.buildDir
30 | }
31 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/bus/MainBus.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.bus;
2 |
3 | import com.github.florent37.rxsharedpreferences.RxBus;
4 |
5 | import java.util.concurrent.TimeUnit;
6 |
7 | import io.reactivex.Observable;
8 |
9 | /**
10 | * Created by florentchampigny on 20/06/2017.
11 | */
12 |
13 | public class MainBus extends RxBus {
14 |
15 | public void displayApp(String name) {
16 | super.post(new DisplayAppEvent(name));
17 | }
18 |
19 | public Observable onDisplayApp() {
20 | return super.onEvent(DisplayAppEvent.class)
21 | .map(e -> e.name)
22 | .throttleWithTimeout(1, TimeUnit.SECONDS);
23 | }
24 |
25 | class DisplayAppEvent {
26 | public String name;
27 |
28 | public DisplayAppEvent(String name) {
29 | this.name = name;
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/android/app/src/androidTest/java/florent37/github/com/mam/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("florent37.github.com.mam", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/MainApplication.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam;
2 |
3 | import android.app.Application;
4 |
5 | import com.facebook.stetho.Stetho;
6 |
7 | import florent37.github.com.mam.dagger.AppComponent;
8 | import florent37.github.com.mam.dagger.AppModule;
9 | import florent37.github.com.mam.dagger.DaggerAppComponent;
10 |
11 | /**
12 | * Created by florentchampigny on 18/06/2017.
13 | */
14 |
15 | public class MainApplication extends Application{
16 | private AppComponent appComponent;
17 |
18 | @Override
19 | public void onCreate() {
20 | super.onCreate();
21 |
22 | this.appComponent = DaggerAppComponent.builder()
23 | .appModule(new AppModule(this))
24 | .build();
25 |
26 | Stetho.initializeWithDefaults(this);
27 | }
28 |
29 | public AppComponent getAppComponent() {
30 | return appComponent;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 |
5 | # Files for the ART/Dalvik VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # Generated files
12 | bin/
13 | gen/
14 | out/
15 |
16 | # Gradle files
17 | .gradle/
18 | build/
19 |
20 | # Local configuration file (sdk path, etc)
21 | local.properties
22 |
23 | # Proguard folder generated by Eclipse
24 | proguard/
25 |
26 | # Log Files
27 | *.log
28 |
29 | # Android Studio Navigation editor temp files
30 | .navigation/
31 |
32 | # Android Studio captures folder
33 | captures/
34 |
35 | # Intellij
36 | *.iml
37 | .idea/workspace.xml
38 | .idea/tasks.xml
39 | .idea/gradle.xml
40 | .idea/dictionaries
41 | .idea/libraries
42 |
43 | # Keystore files
44 | *.jks
45 |
46 | # External native build folder generated in Android Studio 2.2 and later
47 | .externalNativeBuild
48 |
49 | # Google Services (e.g. APIs or Firebase)
50 | google-services.json
51 |
52 | # Freeline
53 | freeline.py
54 | freeline/
55 | freeline_project_description.json
56 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/android/app/src/prod/java/mam/repository/AppRepositoryImpl.java:
--------------------------------------------------------------------------------
1 | package mam.repository;
2 |
3 |
4 | import java.util.List;
5 |
6 | import javax.inject.Inject;
7 | import javax.inject.Singleton;
8 |
9 | import florent37.github.com.mam.api.MamAPI;
10 | import florent37.github.com.mam.model.App;
11 | import florent37.github.com.mam.model.AppResponse;
12 | import florent37.github.com.mam.model.AppVersion;
13 | import florent37.github.com.mam.model.AppsResponse;
14 | import florent37.github.com.mam.repository.AppRepository;
15 | import io.reactivex.Single;
16 |
17 | public class AppRepositoryImpl implements AppRepository {
18 |
19 | private final MamAPI api;
20 |
21 | @Inject
22 | public AppRepositoryImpl(MamAPI api) {
23 | this.api = api;
24 | }
25 |
26 |
27 | public Single> applications() {
28 | return api.apps().map(AppsResponse::getApps);
29 | }
30 |
31 | public Single> application(String name) {
32 | return api.app(name).map(AppResponse::getVersions);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/florentchampigny/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/dagger/AppComponent.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.dagger;
2 |
3 | import android.content.Context;
4 | import android.support.annotation.NonNull;
5 |
6 | import javax.inject.Singleton;
7 |
8 | import dagger.Component;
9 | import florent37.github.com.mam.ui.apps.AppsActivity;
10 | import florent37.github.com.mam.MainApplication;
11 | import florent37.github.com.mam.ui.apps.AppsListFragment;
12 | import florent37.github.com.mam.ui.versions.VersionsListFragment;
13 |
14 | /**
15 | * Created by florentchampigny on 18/05/2017.
16 | */
17 |
18 | @Component(modules = AppModule.class)
19 | @Singleton
20 | public abstract class AppComponent {
21 |
22 | public static AppComponent from(@NonNull Context context){
23 | return ((MainApplication) context.getApplicationContext()).getAppComponent();
24 | }
25 |
26 | public abstract void inject(AppsActivity appsActivity);
27 |
28 | public abstract void inject(AppsListFragment appsListFragment);
29 |
30 | public abstract void inject(VersionsListFragment versionsListFragment);
31 | }
32 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/ic_play_store.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/ic_play_store_black.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/ui/versions/VersionsActivity.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.ui.versions;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 |
7 | import florent37.github.com.mam.R;
8 | import florent37.github.com.mam.common.BaseActivity;
9 | import florent37.github.com.mam.model.App;
10 |
11 | public class VersionsActivity extends BaseActivity {
12 |
13 | static final String APP = "APP";
14 |
15 | public static Intent newInstance(Context context, App app){
16 | return new Intent(context, VersionsActivity.class)
17 | .putExtra(APP, app);
18 | }
19 |
20 | @Override
21 | protected void onCreate(Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 | setContentView(R.layout.activity_main);
24 | displayApp((App) getIntent().getSerializableExtra(APP));
25 | }
26 |
27 | public void displayApp(App app) {
28 | getSupportFragmentManager().beginTransaction()
29 | .replace(R.id.content, VersionsListFragment.newInstance(app))
30 | .commitAllowingStateLoss();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/website/php/database/db.php:
--------------------------------------------------------------------------------
1 | 'SET NAMES utf8',
16 | PDO::ATTR_PERSISTENT => true
17 | )); // connexion à la BDD
18 | }
19 |
20 | return $database;
21 | }
22 |
23 | function createDatabase()
24 | {
25 | $dbhost = "database"; //identique à l'alias docker
26 | $dbuser = "root";
27 | $dbpassword = "password";
28 |
29 | try {
30 | $dbh = new PDO("mysql:host=$dbhost;port=3306", $dbuser, $dbpassword);
31 | $dbh->exec(getSqlFile("database/APPLICATION.sql"));
32 | } catch (PDOException $e) {
33 | die("DB ERROR: " . $e->getMessage());
34 | }
35 | }
36 |
37 | function getSqlFile($fileName)
38 | {
39 | return file_get_contents(ROOT_DIR . $fileName);
40 | }
41 |
42 | ?>
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/ui/apps/AppsHeaderViewHolder.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.ui.apps;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 |
8 | import butterknife.ButterKnife;
9 | import florent37.github.com.mam.R;
10 | import florent37.github.com.mam.common.ClickListenerWrapper;
11 | import florent37.github.com.mam.ui.versions.VersionsAdapter;
12 |
13 | /**
14 | * Created by florentchampigny on 20/06/2017.
15 | */
16 |
17 | public class AppsHeaderViewHolder extends RecyclerView.ViewHolder {
18 |
19 | public static RecyclerView.ViewHolder build(ViewGroup parent, ClickListenerWrapper clickListenerClickListenerWrapper) {
20 | return new AppsHeaderViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.app_header_cell, parent, false), clickListenerClickListenerWrapper);
21 | }
22 |
23 | private ClickListenerWrapper clickListenerWrapper;
24 |
25 | public AppsHeaderViewHolder(View itemView, ClickListenerWrapper clickListenerWrapper) {
26 | super(itemView);
27 | ButterKnife.bind(this, itemView);
28 | this.clickListenerWrapper = clickListenerWrapper;
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/ui/apps/AppsPresenter.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.ui.apps;
2 |
3 | import java.util.List;
4 |
5 | import javax.inject.Inject;
6 |
7 | import florent37.github.com.mam.bus.MainBus;
8 | import florent37.github.com.mam.common.AbstractPresenter;
9 | import florent37.github.com.mam.model.App;
10 | import florent37.github.com.mam.repository.AppRepository;
11 |
12 | public class AppsPresenter extends AbstractPresenter {
13 |
14 | private final AppRepository appRepository;
15 | private final MainBus mainBus;
16 |
17 | @Inject
18 | public AppsPresenter(AppRepository appRepository, MainBus mainBus) {
19 | this.appRepository = appRepository;
20 | this.mainBus = mainBus;
21 | }
22 |
23 | public void start() {
24 | appRepository.applications()
25 | .compose(super.>compose())
26 | .subscribe(
27 | apps ->
28 | getView().displayApps(apps),
29 | throwable -> {
30 | throwable.printStackTrace();
31 | });
32 | }
33 |
34 | public void onAppClicked(App app) {
35 | getView().displayApp(app);
36 | }
37 |
38 | public interface View extends AbstractPresenter.View {
39 | void displayApps(List apps);
40 |
41 | void displayApp(App app);
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/ui/versions/VersionsHeaderViewHolder.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.ui.versions;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.ImageView;
8 | import android.widget.TextView;
9 |
10 | import butterknife.BindView;
11 | import butterknife.ButterKnife;
12 | import florent37.github.com.mam.R;
13 | import florent37.github.com.mam.common.ClickListenerWrapper;
14 | import florent37.github.com.mam.model.App;
15 |
16 | /**
17 | * Created by florentchampigny on 20/06/2017.
18 | */
19 |
20 | public class VersionsHeaderViewHolder extends RecyclerView.ViewHolder {
21 |
22 | public static RecyclerView.ViewHolder build(ViewGroup parent, ClickListenerWrapper clickListenerClickListenerWrapper) {
23 | return new VersionsHeaderViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.appversion_header_cell, parent, false), clickListenerClickListenerWrapper);
24 | }
25 |
26 | private ClickListenerWrapper clickListenerWrapper;
27 |
28 | public VersionsHeaderViewHolder(View itemView, ClickListenerWrapper clickListenerWrapper) {
29 | super(itemView);
30 | ButterKnife.bind(this, itemView);
31 | this.clickListenerWrapper = clickListenerWrapper;
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/model/AppVersion.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.model;
2 |
3 | /**
4 | * Created by florentchampigny on 18/06/2017.
5 | */
6 |
7 | public class AppVersion {
8 | private String date;
9 | private String version;
10 | private String code;
11 | private String url;
12 | private String comment;
13 |
14 | public AppVersion() {
15 | }
16 |
17 | public AppVersion(String version, String code, String date, String url) {
18 | this.date = date;
19 | this.version = version;
20 | this.code = code;
21 | this.url = url;
22 | }
23 |
24 | public String getDate() {
25 | return date;
26 | }
27 |
28 | public void setDate(String date) {
29 | this.date = date;
30 | }
31 |
32 | public String getVersion() {
33 | return version;
34 | }
35 |
36 | public void setVersion(String version) {
37 | this.version = version;
38 | }
39 |
40 | public String getCode() {
41 | return code;
42 | }
43 |
44 | public void setCode(String code) {
45 | this.code = code;
46 | }
47 |
48 | public String getUrl() {
49 | return url;
50 | }
51 |
52 | public void setUrl(String url) {
53 | this.url = url;
54 | }
55 |
56 | public String getComment() {
57 | return comment;
58 | }
59 |
60 | public void setComment(String comment) {
61 | this.comment = comment;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/ui/versions/VersionsPresenter.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.ui.versions;
2 |
3 | import java.util.List;
4 |
5 | import javax.inject.Inject;
6 |
7 | import florent37.github.com.mam.common.AbstractPresenter;
8 | import florent37.github.com.mam.model.App;
9 | import florent37.github.com.mam.model.AppVersion;
10 | import florent37.github.com.mam.repository.AppRepository;
11 |
12 | public class VersionsPresenter extends AbstractPresenter {
13 |
14 | private final AppRepository appRepository;
15 | private App app;
16 |
17 | @Inject
18 | public VersionsPresenter(AppRepository appRepository) {
19 | this.appRepository = appRepository;
20 | }
21 |
22 | public void init(App app){
23 | this.app = app;
24 | }
25 |
26 | public void start() {
27 | appRepository.application(app.getName())
28 | .compose(super.>compose())
29 | .subscribe(
30 | versions -> getView().displayAppVersions(versions),
31 | throwable -> {
32 | });
33 |
34 | getView().displayAppName(app.getPackageName());
35 | }
36 |
37 | public void onVersionClicked(AppVersion appVersion) {
38 |
39 | }
40 |
41 | public interface View extends AbstractPresenter.View {
42 | void displayAppVersions(List versions);
43 |
44 | void displayAppName(String name);
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/website/php/database/APPLICATION.sql:
--------------------------------------------------------------------------------
1 | CREATE DATABASE IF NOT EXISTS APPLICATION;
2 | USE APPLICATION;
3 |
4 | SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
5 | SET time_zone = "+00:00";
6 |
7 | --
8 | -- Database: `APPLICATION`
9 | --
10 |
11 | -- --------------------------------------------------------
12 |
13 | --
14 | -- Table structure for table `APPLICATION`
15 | --
16 |
17 | CREATE TABLE IF NOT EXISTS `APPLICATION` (
18 | `APP_ID` int(11) NOT NULL,
19 | `APP_NAME` varchar(32) NOT NULL,
20 | `APP_PACKAGE` varchar(64) NOT NULL,
21 | `APP_IMAGE` varchar(64) DEFAULT NULL
22 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
23 |
24 | -- --------------------------------------------------------
25 |
26 | --
27 | -- Table structure for table `VERSION`
28 | --
29 |
30 | CREATE TABLE IF NOT EXISTS `VERSION` (
31 | `ID` int(11) NOT NULL,
32 | `VERSION` varchar(12) NOT NULL,
33 | `CODE` int(11) NOT NULL,
34 | `URL` varchar(64) NOT NULL,
35 | `COMMENT` varchar(164) NOT NULL,
36 | `DATE` datetime NOT NULL,
37 | `APP_ID` int(11) NOT NULL
38 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
39 |
40 | --
41 | -- Indexes for dumped tables
42 | --
43 |
44 | ALTER TABLE `APPLICATION`
45 | ADD PRIMARY KEY (`APP_ID`),
46 | ADD UNIQUE KEY `APP_ID` (`APP_ID`),
47 | ADD UNIQUE KEY `APP_PACKAGE` (`APP_PACKAGE`);
48 |
49 | ALTER TABLE `VERSION`
50 | ADD PRIMARY KEY (`ID`);
51 |
52 |
53 | ALTER TABLE `APPLICATION`
54 | MODIFY `APP_ID` int(11) NOT NULL AUTO_INCREMENT;
55 |
56 | ALTER TABLE `VERSION`
57 | MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Open-Mam
2 |
3 | Open-Mam is an open-source mobile application management,
4 | With this project, you will have your own mobile store, linked with your own server.
5 |
6 | The main goal is to distribute your applications to your employers, your beta-testers or your clients,
7 | but without sending your application binaries on the cloud, you will handle their storage.
8 |
9 | # API
10 |
11 | The api has been thinked to be used on any server, even in :
12 | - a hosted server (PHP)
13 | - a mutualised server (PHP)
14 | - a server that does not allow url-rewriting
15 | - a server without database (use file mode or sqllite)
16 | - a server that deny file storage (allow to store apk's outside, eg: in a git or a ftp)
17 |
18 | # Android
19 |
20 | The Android application is like the Play Store, but contains only your own applications,
21 | it's built with open-source libraries, and has a material look & feel.
22 |
23 | To have an Android store, you can :
24 | - use the given application binary (link in this README), and link it to you own server
25 | or
26 | - fork it, then modify the `configuration.gradle` file to change his name, his colors and directly couple with your server
27 |
28 | # Roadmap
29 |
30 | There's a lot of work to publish this project, I'm currently working on :
31 |
32 | - Implementing the API (PHP / Mysql)
33 | - Developping the Android Client
34 | - Developping the front website
35 | - Versioning the API
36 | - Versioning the Android Client
37 | - Adding notifications on Android Client
38 |
39 | # Contribution
40 |
41 | Feel free to contribute to this project by sending me pull requests or issues
42 | Contact me on my email champigny.florent@gmail.com if you have any question about Open-Mam
43 |
--------------------------------------------------------------------------------
/android/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/android/app/upload.gradle:
--------------------------------------------------------------------------------
1 | android.applicationVariants.all { variant ->
2 | buildConfigField "String", "MAM_SERVER_URL", "\""+project.mamServer+"\""
3 | variant.outputs.each { output ->
4 | def t = task("upload${variant.name.capitalize()}") << {
5 | def apkFile = output.outputFile;
6 | def applicationId = [variant.mergedFlavor.applicationId, variant.buildType.applicationIdSuffix].findAll().join()
7 |
8 | println "uploading $applicationId : $apkFile.path"
9 |
10 | def serverUrl = project.mamServer
11 |
12 | def uri = "http://$serverUrl/api.php?method=upload"
13 | uri = uri + '&package=' + applicationId
14 | uri = uri + '&name=' + project.mapAppName
15 | uri = uri + '&apkName=' + apkFile.name
16 | uri = uri + '&code=' + android.defaultConfig.versionCode
17 | uri = uri + '&version=' + android.defaultConfig.versionName
18 |
19 | def url = new URL(uri)
20 | def http = url.openConnection()
21 |
22 | http.setDoOutput(true)
23 | http.setRequestMethod('POST')
24 | http.setRequestProperty('User-agent', 'gradle upload script')
25 |
26 | def out = http.outputStream
27 | def input = new FileInputStream(apkFile)
28 |
29 | try {
30 | byte[] buf = new byte[1024];
31 | int len;
32 | while((len=input.read(buf))>0){
33 | out.write(buf,0,len);
34 | }
35 | out.close();
36 | input.close();
37 | } catch (Exception e) {
38 | e.printStackTrace();
39 | }
40 |
41 | def httpResponse = http.inputStream.text // read server response from it
42 | println httpResponse
43 | }
44 | t.dependsOn "assemble${variant.name.capitalize()}"
45 | t.group = "MAM"
46 | }
47 | }
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/model/App.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.model;
2 |
3 | import java.io.Serializable;
4 |
5 | /**
6 | * Created by florentchampigny on 18/06/2017.
7 | */
8 |
9 | public class App implements Serializable {
10 |
11 | private String name;
12 | private String lastVersion;
13 | private String image;
14 | private String dateLastUpdate;
15 | private String packageName;
16 | private String lastCode;
17 |
18 | public App() {
19 | }
20 |
21 | public App(String name, String lastVersion, String image, String dateLastUpdate) {
22 | this.name = name;
23 | this.packageName = name;
24 | this.lastVersion = lastVersion;
25 | this.image = image;
26 | this.dateLastUpdate = dateLastUpdate;
27 | }
28 |
29 | public String getName() {
30 | return name;
31 | }
32 |
33 | public void setName(String name) {
34 | this.name = name;
35 | }
36 |
37 | public String getLastVersion() {
38 | return lastVersion;
39 | }
40 |
41 | public void setLastVersion(String lastVersion) {
42 | this.lastVersion = lastVersion;
43 | }
44 |
45 | public String getImage() {
46 | return image;
47 | }
48 |
49 | public void setImage(String image) {
50 | this.image = image;
51 | }
52 |
53 | public String getDateLastUpdate() {
54 | return dateLastUpdate;
55 | }
56 |
57 | public void setDateLastUpdate(String dateLastUpdate) {
58 | this.dateLastUpdate = dateLastUpdate;
59 | }
60 |
61 | public String getLastCode() {
62 | return lastCode;
63 | }
64 |
65 | public void setLastCode(String lastCode) {
66 | this.lastCode = lastCode;
67 | }
68 |
69 | public String getPackageName() {
70 | return packageName;
71 | }
72 |
73 | public void setPackageName(String packageName) {
74 | this.packageName = packageName;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/apps_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
22 |
23 |
28 |
29 |
36 |
37 |
42 |
43 |
44 |
45 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/ui/versions/VersionsViewHolder.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.ui.versions;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.TextView;
8 |
9 | import butterknife.BindView;
10 | import butterknife.ButterKnife;
11 | import butterknife.OnClick;
12 | import florent37.github.com.mam.R;
13 | import florent37.github.com.mam.common.ClickListenerWrapper;
14 | import florent37.github.com.mam.model.AppVersion;
15 |
16 | /**
17 | * Created by florentchampigny on 20/06/2017.
18 | */
19 |
20 | public class VersionsViewHolder extends RecyclerView.ViewHolder {
21 |
22 | @BindView(R.id.date)
23 | TextView date;
24 | @BindView(R.id.version)
25 | TextView version;
26 | @BindView(R.id.code)
27 | TextView code;
28 | private AppVersion appVersion;
29 | private ClickListenerWrapper clickListenerWrapper;
30 | public VersionsViewHolder(View itemView, ClickListenerWrapper clickListenerWrapper) {
31 | super(itemView);
32 | ButterKnife.bind(this, itemView);
33 | this.clickListenerWrapper = clickListenerWrapper;
34 | }
35 |
36 | public static RecyclerView.ViewHolder build(ViewGroup parent, ClickListenerWrapper clickListenerClickListenerWrapper) {
37 | return new VersionsViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.appversion_cell, parent, false), clickListenerClickListenerWrapper);
38 | }
39 |
40 | @OnClick(R.id.download)
41 | public void onLayoutClicked() {
42 | clickListenerWrapper.getListener(listener ->
43 | listener.onAppClicked(appVersion)
44 | );
45 | }
46 |
47 | public void bind(final AppVersion appVersion) {
48 | this.appVersion = appVersion;
49 |
50 | this.date.setText(appVersion.getDate());
51 | this.code.setText(String.format("(%s)", appVersion.getCode()));
52 | this.version.setText(appVersion.getVersion());
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/dagger/AppModule.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.dagger;
2 |
3 | import android.app.Application;
4 |
5 | import com.facebook.stetho.okhttp3.StethoInterceptor;
6 |
7 | import javax.inject.Singleton;
8 |
9 | import dagger.Module;
10 | import dagger.Provides;
11 | import florent37.github.com.mam.BuildConfig;
12 | import florent37.github.com.mam.api.MamAPI;
13 | import florent37.github.com.mam.bus.MainBus;
14 | import florent37.github.com.mam.repository.AppRepository;
15 | import mam.repository.AppRepositoryImpl;
16 | import okhttp3.OkHttpClient;
17 | import okhttp3.logging.HttpLoggingInterceptor;
18 | import retrofit2.Retrofit;
19 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
20 | import retrofit2.converter.gson.GsonConverterFactory;
21 |
22 | /**
23 | * Created by florentchampigny on 18/05/2017.
24 | */
25 |
26 | @Module
27 | public class AppModule {
28 |
29 | private final Application application;
30 |
31 | public AppModule(Application application) {
32 | this.application = application;
33 | }
34 |
35 | @Provides
36 | @Singleton
37 | public AppRepository provideAppsRepository(MamAPI mamAPI) {
38 | return new AppRepositoryImpl(mamAPI);
39 | }
40 |
41 | @Provides
42 | @Singleton
43 | public MainBus provideMainBus() {
44 | return new MainBus();
45 | }
46 |
47 | @Provides
48 | @Singleton
49 | public OkHttpClient provideMainOkHttp() {
50 | return new OkHttpClient.Builder()
51 | .addInterceptor(new StethoInterceptor())
52 | .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
53 | .build();
54 | }
55 |
56 | @Provides
57 | @Singleton
58 | public MamAPI providesGithubApi(OkHttpClient okHttpClient) {
59 | return new Retrofit.Builder()
60 | .client(okHttpClient)
61 | .baseUrl(BuildConfig.MAM_SERVER_URL)
62 | .addConverterFactory(GsonConverterFactory.create())
63 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
64 | .build()
65 | .create(MamAPI.class);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/common/AbstractPresenter.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.common;
2 |
3 | import android.arch.lifecycle.LifecycleRegistry;
4 |
5 | import java.lang.ref.WeakReference;
6 |
7 | import florent37.github.com.rxlifecycle.RxLifecycle;
8 | import io.reactivex.Single;
9 | import io.reactivex.SingleSource;
10 | import io.reactivex.SingleTransformer;
11 | import io.reactivex.android.schedulers.AndroidSchedulers;
12 | import io.reactivex.annotations.NonNull;
13 | import io.reactivex.disposables.CompositeDisposable;
14 | import io.reactivex.disposables.Disposable;
15 | import io.reactivex.schedulers.Schedulers;
16 |
17 | /**
18 | * Created by florentchampigny on 20/06/2017.
19 | */
20 |
21 | public class AbstractPresenter {
22 |
23 | private final CompositeDisposable compositeDisposable;
24 | private WeakReference viewReference;
25 |
26 | public AbstractPresenter() {
27 | compositeDisposable = new CompositeDisposable();
28 | }
29 |
30 | protected void call(Disposable disposable) {
31 | compositeDisposable.add(disposable);
32 | }
33 |
34 | protected V getView() {
35 | return viewReference.get();
36 | }
37 |
38 | public void bind(LifecycleRegistry lifecycle, V view) {
39 | unbind();
40 | this.viewReference = new WeakReference(view);
41 | RxLifecycle.with(lifecycle)
42 | .onDestroy()
43 | .doOnSubscribe(this::call)
44 | .subscribe(x -> unbind());
45 | }
46 |
47 | private void unbind() {
48 | compositeDisposable.clear();
49 | if (viewReference != null) {
50 | viewReference.clear();
51 | }
52 | }
53 |
54 | public SingleTransformer super R, ? extends R> compose() {
55 | return new SingleTransformer() {
56 | @Override
57 | public SingleSource apply(@NonNull Single upstream) {
58 | return upstream
59 | .subscribeOn(Schedulers.io())
60 | .observeOn(AndroidSchedulers.mainThread())
61 | .doOnSubscribe(AbstractPresenter.this::call);
62 | }
63 | };
64 | }
65 |
66 | public interface View {
67 |
68 | }
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/ui/apps/AppsAdapter.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.ui.apps;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.ViewGroup;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | import florent37.github.com.mam.common.ClickListenerWrapper;
10 | import florent37.github.com.mam.model.App;
11 |
12 | public class AppsAdapter extends RecyclerView.Adapter {
13 |
14 | private final int HEADER_SIZE = 1;
15 | private final int TYPE_HEADER = 1;
16 | private final int TYPE_CELL = 2;
17 |
18 | private List appList = new ArrayList<>();
19 | private ClickListenerWrapper clickListenerWrapper = new ClickListenerWrapper<>();
20 |
21 | @Override
22 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
23 | switch (viewType){
24 | case TYPE_CELL:
25 | return AppViewHolder.build(parent, clickListenerWrapper);
26 | case TYPE_HEADER:
27 | return AppsHeaderViewHolder.build(parent, clickListenerWrapper);
28 | }
29 | return null;
30 | }
31 |
32 | @Override
33 | public int getItemViewType(int position) {
34 | if(position < HEADER_SIZE){
35 | return TYPE_HEADER;
36 | }
37 | return TYPE_CELL;
38 | }
39 |
40 | @Override
41 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
42 | if(position >= HEADER_SIZE) {
43 | if (holder instanceof AppViewHolder) {
44 | ((AppViewHolder) holder).bind(getItem(position));
45 | }
46 | }
47 | }
48 |
49 | public AppsAdapter onClick(ClickListener clickListener){
50 | this.clickListenerWrapper.setListener(clickListener);
51 | return this;
52 | }
53 |
54 |
55 | public App getItem(int position) {
56 | return appList.get(position - HEADER_SIZE);
57 | }
58 |
59 | @Override
60 | public int getItemCount() {
61 | return appList.size() + HEADER_SIZE;
62 | }
63 |
64 | public void setItems(List items) {
65 | this.appList.clear();
66 | this.appList.addAll(items);
67 | notifyDataSetChanged();
68 | }
69 |
70 | public interface ClickListener {
71 | void onAppClicked(App app);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/appversion_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
16 |
17 |
20 |
21 |
26 |
27 |
34 |
35 |
46 |
47 |
48 |
49 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/ui/versions/VersionsAdapter.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.ui.versions;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.ViewGroup;
5 |
6 | import java.util.ArrayList;
7 | import java.util.List;
8 |
9 | import florent37.github.com.mam.common.ClickListenerWrapper;
10 | import florent37.github.com.mam.model.AppVersion;
11 |
12 | public class VersionsAdapter extends RecyclerView.Adapter {
13 |
14 | private final int HEADER_SIZE = 1;
15 | private final int TYPE_HEADER = 1;
16 | private final int TYPE_CELL = 2;
17 |
18 | private List appList = new ArrayList<>();
19 | private ClickListenerWrapper clickListenerWrapper = new ClickListenerWrapper<>();
20 |
21 | @Override
22 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
23 | switch (viewType){
24 | case TYPE_CELL:
25 | return VersionsViewHolder.build(parent, clickListenerWrapper);
26 | case TYPE_HEADER:
27 | return VersionsHeaderViewHolder.build(parent, clickListenerWrapper);
28 | }
29 | return null;
30 | }
31 |
32 | @Override
33 | public int getItemViewType(int position) {
34 | if(position < HEADER_SIZE){
35 | return TYPE_HEADER;
36 | }
37 | return TYPE_CELL;
38 | }
39 |
40 | @Override
41 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
42 | if(position >= HEADER_SIZE) {
43 | if (holder instanceof VersionsViewHolder) {
44 | ((VersionsViewHolder) holder).bind(getItem(position));
45 | }
46 | }
47 | }
48 |
49 | public VersionsAdapter onClick(ClickListener clickListener){
50 | this.clickListenerWrapper.setListener(clickListener);
51 | return this;
52 | }
53 |
54 |
55 | private AppVersion getItem(int position) {
56 | return appList.get(position - HEADER_SIZE);
57 | }
58 |
59 | @Override
60 | public int getItemCount() {
61 | return appList.size() + HEADER_SIZE;
62 | }
63 |
64 | public void setItems(List items) {
65 | this.appList.clear();
66 | this.appList.addAll(items);
67 | notifyDataSetChanged();
68 | }
69 |
70 | public interface ClickListener {
71 | void onAppClicked(AppVersion appVersion);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/ui/apps/AppsListFragment.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.ui.apps;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v4.app.Fragment;
6 | import android.support.v7.widget.LinearLayoutManager;
7 | import android.support.v7.widget.RecyclerView;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 |
12 | import java.util.List;
13 |
14 | import javax.inject.Inject;
15 |
16 | import butterknife.BindView;
17 | import butterknife.ButterKnife;
18 | import florent37.github.com.mam.R;
19 | import florent37.github.com.mam.common.BaseFragment;
20 | import florent37.github.com.mam.dagger.*;
21 | import florent37.github.com.mam.model.App;
22 | import florent37.github.com.mam.ui.versions.VersionsActivity;
23 |
24 | public class AppsListFragment extends BaseFragment implements AppsPresenter.View {
25 |
26 | @BindView(R.id.recyclerView)
27 | RecyclerView recyclerView;
28 |
29 | @Inject
30 | AppsPresenter presenter;
31 |
32 | @BindView(R.id.apps_header)
33 | View header;
34 |
35 | @Override
36 | public void onCreate(@Nullable Bundle savedInstanceState) {
37 | super.onCreate(savedInstanceState);
38 | AppComponent.from(getContext()).inject(this);
39 | presenter.bind(getLifecycle(), this);
40 | }
41 |
42 | @Nullable
43 | @Override
44 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
45 | return inflater.inflate(R.layout.apps_list, container, false);
46 | }
47 |
48 | @Override
49 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
50 | super.onViewCreated(view, savedInstanceState);
51 | ButterKnife.bind(this, view);
52 |
53 | recyclerView.setAdapter(new AppsAdapter().onClick(presenter::onAppClicked));
54 | recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
55 |
56 | presenter.start();
57 |
58 | recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
59 |
60 | int y = 0;
61 |
62 | @Override
63 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
64 | super.onScrolled(recyclerView, dx, dy);
65 | this.y -= dy;
66 |
67 | header.setTranslationY(y/2f);
68 | }
69 | });
70 | }
71 |
72 | public static Fragment newInstance() {
73 | return new AppsListFragment();
74 | }
75 |
76 | @Override
77 | public void displayApps(List apps) {
78 | ((AppsAdapter) recyclerView.getAdapter()).setItems(apps);
79 | }
80 |
81 | @Override
82 | public void displayApp(App app) {
83 | startActivity(VersionsActivity.newInstance(getContext(), app));
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/website/php/repository/AppRepositoryDb.php:
--------------------------------------------------------------------------------
1 | prepare( "SELECT a.*, max(v.CODE) as lastCode, v.version as lastVersion FROM APPLICATION a, VERSION v WHERE a.APP_ID = v.APP_ID"); //verify user access, for now give him all apps
14 | $requete->execute();
15 |
16 | $apps = [];
17 |
18 | while($row = $requete->fetch( PDO::FETCH_NAMED )){
19 | $app = new App();
20 |
21 | $app->name = $row["APP_NAME"];
22 | $app->packageName = $row["APP_PACKAGE"];
23 | $app->dateLastUpdate = $row["dateLastUpdate"];
24 | $app->image = $row["APP_IMAGE"];
25 | $app->lastCode = $row["lastCode"];
26 | $app->lastVersion = $row["lastVersion"];
27 |
28 | $apps[] = $app;
29 | }
30 | $db = null;
31 |
32 | return $apps;
33 | }
34 |
35 | public function provideApplication($name)
36 | {
37 | $db = getConnection();
38 |
39 | $requete=$db->prepare("SELECT * from APPLICATION a , VERSION v WHERE a.APP_NAME=? AND a.APP_ID = v.APP_ID"); //verify user access, for now give him all apps
40 | $requete->execute(
41 | array($name)
42 | );
43 |
44 | //TODO limit
45 |
46 | $versions = [];
47 |
48 | while($row = $requete->fetch( PDO::FETCH_NAMED )){
49 | $version = new AppVersion();
50 |
51 | $version->code = $row["CODE"];
52 | $version->comment = $row["COMMENT"];
53 | $version->date = $row["date"];
54 | $version->url = $row["URL"];
55 | $version->version = $row["VERSION"];
56 |
57 | $versions[] = $version;
58 | }
59 | $db = null;
60 |
61 | return $versions;
62 | }
63 |
64 | public function addAppVersion($appName, $package, $appVersion, $appCode, $comment, $fileUrl)
65 | {
66 | $db = getConnection();
67 |
68 | $sql = "INSERT INTO APPLICATION (APP_NAME, APP_PACKAGE) VALUES (?, ?)";
69 |
70 | $requete = $db->prepare($sql);
71 | $requete->execute(array($appName, $package));
72 | $app_id = $db->lastInsertId();
73 |
74 | if($app_id == 0){
75 | $sql = "SELECT APP_ID FROM APPLICATION WHERE APP_NAME = ?";
76 | $requete = $db->prepare($sql);
77 | $requete->execute(array($appName));
78 | $app_id = $requete->fetch()["APP_ID"];
79 | }
80 |
81 | $sql = "INSERT INTO VERSION (VERSION, CODE, URL, APP_ID, DATE) VALUES (?, ?, ?, ?, NOW())";
82 |
83 | $requete = $db->prepare($sql);
84 | $requete->execute(array($appVersion, $appCode, $fileUrl, $app_id));
85 |
86 | $db = null;
87 | }
88 | }
--------------------------------------------------------------------------------
/android/app/src/mock/java/mam/repository/AppRepositoryImpl.java:
--------------------------------------------------------------------------------
1 | package mam.repository;
2 |
3 |
4 | import java.util.Arrays;
5 | import java.util.List;
6 |
7 | import florent37.github.com.mam.api.MamAPI;
8 | import florent37.github.com.mam.model.App;
9 | import florent37.github.com.mam.model.AppVersion;
10 | import florent37.github.com.mam.repository.AppRepository;
11 | import io.reactivex.Single;
12 |
13 | public class AppRepositoryImpl implements AppRepository {
14 |
15 | public AppRepositoryImpl(MamAPI mamAPI) {
16 |
17 | }
18 |
19 | public Single> applications() {
20 | return Single.fromCallable(() -> Arrays.asList(
21 | new App("Facebook", "1.0.0", "https://lh3.googleusercontent.com/ZZPdzvlpK9r_Df9C3M7j1rNRi7hhHRvPhlklJ3lfi5jk86Jd1s0Y5wcQ1QgbVaAP5Q=w300", "00/00/0000"),
22 | new App("Facebook", "1.0.0", "https://lh3.googleusercontent.com/ZZPdzvlpK9r_Df9C3M7j1rNRi7hhHRvPhlklJ3lfi5jk86Jd1s0Y5wcQ1QgbVaAP5Q=w300", "00/00/0000"),
23 | new App("Facebook", "1.0.0", "https://lh3.googleusercontent.com/ZZPdzvlpK9r_Df9C3M7j1rNRi7hhHRvPhlklJ3lfi5jk86Jd1s0Y5wcQ1QgbVaAP5Q=w300", "00/00/0000"),
24 | new App("Facebook", "1.0.0", "https://lh3.googleusercontent.com/ZZPdzvlpK9r_Df9C3M7j1rNRi7hhHRvPhlklJ3lfi5jk86Jd1s0Y5wcQ1QgbVaAP5Q=w300", "00/00/0000"),
25 | new App("Facebook", "1.0.0", "https://lh3.googleusercontent.com/ZZPdzvlpK9r_Df9C3M7j1rNRi7hhHRvPhlklJ3lfi5jk86Jd1s0Y5wcQ1QgbVaAP5Q=w300", "00/00/0000"),
26 | new App("Facebook", "1.0.0", "https://lh3.googleusercontent.com/ZZPdzvlpK9r_Df9C3M7j1rNRi7hhHRvPhlklJ3lfi5jk86Jd1s0Y5wcQ1QgbVaAP5Q=w300", "00/00/0000"),
27 | new App("Facebook", "1.0.0", "https://lh3.googleusercontent.com/ZZPdzvlpK9r_Df9C3M7j1rNRi7hhHRvPhlklJ3lfi5jk86Jd1s0Y5wcQ1QgbVaAP5Q=w300", "00/00/0000"),
28 | new App("Facebook", "1.0.0", "https://lh3.googleusercontent.com/ZZPdzvlpK9r_Df9C3M7j1rNRi7hhHRvPhlklJ3lfi5jk86Jd1s0Y5wcQ1QgbVaAP5Q=w300", "00/00/0000"),
29 | new App("Facebook", "1.0.0", "https://lh3.googleusercontent.com/ZZPdzvlpK9r_Df9C3M7j1rNRi7hhHRvPhlklJ3lfi5jk86Jd1s0Y5wcQ1QgbVaAP5Q=w300", "00/00/0000")
30 | ));
31 | }
32 |
33 | public Single> application(String name) {
34 | return Single.fromCallable(() -> Arrays.asList(
35 | new AppVersion("1.0.0", "38", "22/11/2017 23h59", ""),
36 | new AppVersion("1.0.0", "38", "22/11/2017 23h59", ""),
37 | new AppVersion("1.0.0", "38", "22/11/2017 23h59", ""),
38 | new AppVersion("1.0.0", "38", "22/11/2017 23h59", ""),
39 | new AppVersion("1.0.0", "38", "22/11/2017 23h59", ""),
40 | new AppVersion("1.0.0", "38", "22/11/2017 23h59", ""),
41 | new AppVersion("1.0.0", "38", "22/11/2017 23h59", ""),
42 | new AppVersion("1.0.0", "38", "22/11/2017 23h59", ""),
43 | new AppVersion("1.0.0", "38", "22/11/2017 23h59", "")
44 | ));
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/ui/apps/AppViewHolder.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.ui.apps;
2 |
3 | import android.graphics.drawable.Drawable;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.widget.RecyclerView;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.ViewGroup;
9 | import android.widget.ImageView;
10 | import android.widget.TextView;
11 |
12 | import com.bumptech.glide.Glide;
13 | import com.bumptech.glide.load.DataSource;
14 | import com.bumptech.glide.load.engine.GlideException;
15 | import com.bumptech.glide.request.RequestListener;
16 | import com.bumptech.glide.request.target.Target;
17 |
18 | import butterknife.BindView;
19 | import butterknife.ButterKnife;
20 | import butterknife.OnClick;
21 | import florent37.github.com.mam.R;
22 | import florent37.github.com.mam.common.ClickListenerWrapper;
23 | import florent37.github.com.mam.model.App;
24 |
25 | /**
26 | * Created by florentchampigny on 20/06/2017.
27 | */
28 |
29 | public class AppViewHolder extends RecyclerView.ViewHolder {
30 |
31 | public static RecyclerView.ViewHolder build(ViewGroup parent, ClickListenerWrapper clickListenerClickListenerWrapper) {
32 | return new AppViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.app_cell, parent, false), clickListenerClickListenerWrapper);
33 | }
34 |
35 | @BindView(R.id.image)
36 | ImageView image;
37 | @BindView(R.id.title)
38 | TextView title;
39 | @BindView(R.id.letter)
40 | TextView letter;
41 | @BindView(R.id.category)
42 | TextView category;
43 |
44 | private App app;
45 | private ClickListenerWrapper clickListenerWrapper;
46 |
47 | public AppViewHolder(View itemView, ClickListenerWrapper clickListenerWrapper) {
48 | super(itemView);
49 | ButterKnife.bind(this, itemView);
50 | this.clickListenerWrapper = clickListenerWrapper;
51 | }
52 |
53 | @OnClick(R.id.layout)
54 | public void onLayoutClicked(){
55 | clickListenerWrapper.getListener(listener ->
56 | listener.onAppClicked(app)
57 | );
58 | }
59 |
60 | public void bind(final App app){
61 | this.app = app;
62 | this.title.setText(app.getName());
63 | this.category.setText(app.getLastVersion());
64 | letter.setVisibility(View.VISIBLE);
65 | Glide.with(itemView.getContext()).load(app.getImage()).listener(new RequestListener() {
66 | @Override
67 | public boolean onLoadFailed(@Nullable GlideException e, Object model, Target target, boolean isFirstResource) {
68 | return false;
69 | }
70 |
71 | @Override
72 | public boolean onResourceReady(Drawable resource, Object model, Target target, DataSource dataSource, boolean isFirstResource) {
73 | letter.setVisibility(View.INVISIBLE);
74 | return false;
75 | }
76 | }).into(image);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/website/php/controller/AppControllerImpl.php:
--------------------------------------------------------------------------------
1 | appRepository = $appRepository;
21 | $this->fileSaver = $fileSaver;
22 |
23 | $this->init();
24 | }
25 |
26 | public function init()
27 | {
28 | //create DB if necessary
29 | createDatabase();
30 | }
31 |
32 | public function start($params)
33 | {
34 | // index.php ? method=
35 | if (isset($params["method"]))
36 | $method = $params["method"];
37 | else {
38 | displayError("missing param 'method'");
39 | return;
40 | }
41 |
42 | switch ($method) {
43 | case "upload" :
44 | $this->startUpload($params, $files);
45 | break;
46 | case "apps" :
47 | $response = new AppsResponse();
48 | $response->apps = $this->provideApplications();
49 | resultJson($response);
50 | break;
51 | case "app" :
52 | if (isset($params["name"])) {
53 | $response = new AppVersionResponse();
54 | $response->versions = $this->provideApplication($params["name"]);
55 | resultJson($response);
56 | } else {
57 | displayError("missing param 'name'");
58 | }
59 | break;
60 | }
61 | }
62 |
63 | public function provideApplications()
64 | {
65 | $applications = $this->appRepository->provideApplications();
66 | return $applications;
67 | }
68 |
69 | public function provideApplication($name)
70 | {
71 | $appVersions = $this->appRepository->provideApplication($name);
72 | return $appVersions;
73 | }
74 |
75 | function startUpload($params)
76 | {
77 | $appName = $params["name"];
78 | $package = $params["package"];
79 | $apkName = $params["apkName"];
80 | $appVersion = $params["version"];
81 | $appCode = $params["code"];
82 | $comment = $params["comment"];
83 |
84 | //save the apk into tmpFiles
85 |
86 | $outputFileName = "tmpFiles/$apkName";
87 | $putdata = fopen("php://input", "r");
88 | $fp = fopen($outputFileName, "w");
89 | while ($data = fread($putdata, 1024)) {
90 | fwrite($fp, $data);
91 | }
92 | fclose($fp);
93 | fclose($putdata);
94 |
95 | $fileUrl = $this->fileSaver->saveApk($outputFileName);
96 | $this->appRepository->addAppVersion($appName, $package, $appVersion, $appCode, $comment, $fileUrl);
97 |
98 | // TODO: Implement startUpload() method.
99 | }
100 | }
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply from: 'upload.gradle'
3 |
4 | ext{
5 | mapAppName= "MAM"
6 | mamServer= "http://10.0.0.2"
7 | }
8 |
9 | android {
10 | compileSdkVersion 25
11 | buildToolsVersion "25.0.3"
12 | defaultConfig {
13 | applicationId "com.github.florent37.mam"
14 | minSdkVersion 15
15 | targetSdkVersion 25
16 | versionCode 1
17 | versionName "1.0"
18 | jackOptions.enabled = true
19 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
20 | vectorDrawables.useSupportLibrary = true
21 | }
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 | productFlavors {
29 | mock {
30 |
31 | }
32 | prod {
33 |
34 | }
35 | }
36 | compileOptions {
37 | targetCompatibility 1.8
38 | sourceCompatibility 1.8
39 | }
40 | }
41 |
42 | ext {
43 | supportLibVersion = "25.3.1"
44 | archLifecycleVersion = "1.0.0+"
45 | }
46 |
47 | dependencies {
48 | compile fileTree(dir: 'libs', include: ['*.jar'])
49 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
50 | exclude group: 'com.android.support', module: 'support-annotations'
51 | })
52 | compile 'com.android.support:appcompat-v7:' + project.supportLibVersion
53 | compile 'com.android.support:appcompat-v7:' + project.supportLibVersion
54 | compile 'com.android.support:cardview-v7:' + project.supportLibVersion
55 | compile 'com.android.support:recyclerview-v7:' + project.supportLibVersion
56 | compile 'android.arch.lifecycle:extensions:' + project.archLifecycleVersion;
57 | annotationProcessor "android.arch.lifecycle:compiler:" + project.archLifecycleVersion;
58 | compile 'com.github.florent37:rxcomponent-lifecycle:1.0.1@aar'
59 | compile 'com.github.florent37:rxbus:1.0.0'
60 | compile 'com.wada811:android-material-design-colors:3.0.0'
61 |
62 | compile 'com.github.florent37:diagonallayout:1.0.4'
63 |
64 | compile 'com.android.support.constraint:constraint-layout:1.0.2'
65 | testCompile 'junit:junit:4.12'
66 |
67 | compile 'io.reactivex.rxjava2:rxjava:2.0.9'
68 | compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
69 |
70 | compile 'com.jakewharton:butterknife:8.5.1'
71 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
72 |
73 | compile 'com.google.dagger:dagger:2.10'
74 | annotationProcessor 'com.google.dagger:dagger-compiler:2.10'
75 |
76 | compile 'com.github.bumptech.glide:glide:4.0.0-RC0'
77 |
78 | compile 'com.squareup.retrofit2:retrofit:2.3.0'
79 | compile 'com.squareup.retrofit2:converter-gson:2.3.0'
80 | compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
81 |
82 | compile 'com.squareup.okhttp3:okhttp:3.8.1'
83 | compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'
84 |
85 | compile 'com.facebook.stetho:stetho:1.5.0'
86 | compile 'com.facebook.stetho:stetho-okhttp3:1.5.0'
87 |
88 | compile 'com.lapism:searchview:4.0'
89 | compile 'com.flaviofaria:kenburnsview:1.0.7'
90 | }
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/appversion_cell.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
23 |
24 |
31 |
32 |
42 |
43 |
54 |
55 |
65 |
66 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/app_cell.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
23 |
24 |
34 |
35 |
48 |
49 |
58 |
59 |
69 |
70 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/android/app/src/main/java/florent37/github/com/mam/ui/versions/VersionsListFragment.java:
--------------------------------------------------------------------------------
1 | package florent37.github.com.mam.ui.versions;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.Nullable;
5 | import android.support.v7.app.ActionBar;
6 | import android.support.v7.app.AppCompatActivity;
7 | import android.support.v7.widget.LinearLayoutManager;
8 | import android.support.v7.widget.RecyclerView;
9 | import android.support.v7.widget.Toolbar;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.TextView;
14 |
15 | import java.util.List;
16 |
17 | import javax.inject.Inject;
18 |
19 | import butterknife.BindView;
20 | import butterknife.ButterKnife;
21 | import florent37.github.com.mam.R;
22 | import florent37.github.com.mam.common.BaseFragment;
23 | import florent37.github.com.mam.dagger.AppComponent;
24 | import florent37.github.com.mam.model.App;
25 | import florent37.github.com.mam.model.AppVersion;
26 |
27 | public class VersionsListFragment extends BaseFragment implements VersionsPresenter.View {
28 |
29 | static final String APPLICATION = "APPLICATION";
30 | @BindView(R.id.recyclerView)
31 | RecyclerView recyclerView;
32 |
33 | @BindView(R.id.appversions_header)
34 | View header;
35 |
36 | @BindView(R.id.appName)
37 | TextView appName;
38 |
39 | @BindView(R.id.toolbar)
40 | Toolbar toolbar;
41 |
42 | @Inject
43 | VersionsPresenter presenter;
44 |
45 | public static VersionsListFragment newInstance(App app) {
46 | final Bundle args = new Bundle();
47 | args.putSerializable(APPLICATION, app);
48 | VersionsListFragment fragment = new VersionsListFragment();
49 | fragment.setArguments(args);
50 | return fragment;
51 | }
52 |
53 | @Override
54 | public void onCreate(@Nullable Bundle savedInstanceState) {
55 | super.onCreate(savedInstanceState);
56 | AppComponent.from(getContext()).inject(this);
57 | presenter.bind(getLifecycle(), this);
58 | presenter.init((App) getArguments().getSerializable(APPLICATION));
59 | }
60 |
61 | @Nullable
62 | @Override
63 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
64 | return inflater.inflate(R.layout.appversions_list, container, false);
65 | }
66 |
67 | @Override
68 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
69 | super.onViewCreated(view, savedInstanceState);
70 | ButterKnife.bind(this, view);
71 |
72 | AppCompatActivity appCompatActivity = ((AppCompatActivity) getActivity());
73 | appCompatActivity.setSupportActionBar(toolbar);
74 |
75 | final ActionBar supportActionBar = appCompatActivity.getSupportActionBar();
76 | supportActionBar.setDisplayHomeAsUpEnabled(true);
77 | supportActionBar.setHomeButtonEnabled(true);
78 | supportActionBar.setDisplayShowTitleEnabled(false);
79 |
80 | recyclerView.setAdapter(new VersionsAdapter().onClick(presenter::onVersionClicked));
81 | recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
82 |
83 | presenter.start();
84 |
85 | recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
86 |
87 | int y = 0;
88 |
89 | @Override
90 | public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
91 | super.onScrolled(recyclerView, dx, dy);
92 | this.y -= dy;
93 |
94 | header.setTranslationY(y/2f);
95 | }
96 | });
97 | }
98 |
99 | @Override
100 | public void displayAppVersions(List versions) {
101 | ((VersionsAdapter) recyclerView.getAdapter()).setItems(versions);
102 | }
103 |
104 | @Override
105 | public void displayAppName(String name) {
106 | appName.setText(name);
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------