From e7f0038bbda950a52811cb1439cc59025ba69f17 Mon Sep 17 00:00:00 2001 From: Floens Date: Sat, 22 Jul 2017 15:49:45 +0200 Subject: [PATCH] Work on site and new board setup screens. Move to dagger 1 --- Clover/app/build.gradle | 21 +- Clover/app/src/main/assets/icons/4chan.png | Bin 0 -> 344 bytes .../src/main/java/org/floens/chan/Chan.java | 15 +- .../java/org/floens/chan/chan/ChanHelper.java | 4 +- .../chan/core/database/DatabaseHelper.java | 2 + .../chan/core/database/DatabaseManager.java | 12 +- .../core/database/DatabaseSiteManager.java | 29 ++ .../org/floens/chan/core/di/AppModule.java | 86 ++++- .../org/floens/chan/core/di/ChanGraph.java | 131 ------- .../org/floens/chan/core/di/NetModule.java | 8 +- .../floens/chan/core/manager/SiteManager.java | 29 ++ .../chan/core/manager/WatchManager.java | 6 +- .../org/floens/chan/core/model/SiteModel.java | 37 ++ .../chan/core/presenter/SetupPresenter.java | 120 ++++++ .../java/org/floens/chan/core/site/Site.java | 2 + .../org/floens/chan/core/site/SiteIcon.java | 72 ++++ .../chan/core/site/sites/chan4/Chan4.java | 9 +- .../chan/ui/activity/StartActivity.java | 20 +- .../animation}/AnimationUtils.java | 28 +- .../org/floens/chan/ui/cell/PostCell.java | 2 +- .../ui/controller/BoardSetupController.java | 178 +++++++++ .../ui/controller/MainSettingsController.java | 2 +- .../ui/controller/PassSettingsController.java | 2 +- .../ui/controller/SiteSetupController.java | 357 ++++++++++++++++++ .../controller/ThemeSettingsController.java | 3 +- .../floens/chan/ui/layout/FilterLayout.java | 2 +- .../floens/chan/ui/layout/ReplyLayout.java | 2 +- .../chan/ui/layout/ThreadListLayout.java | 2 +- .../chan/ui/settings/SettingsController.java | 2 +- .../org/floens/chan/ui/toolbar/Toolbar.java | 15 +- .../org/floens/chan/ui/view/LoadView.java | 3 +- .../floens/chan/ui/view/ThumbnailView.java | 2 +- .../res/drawable-hdpi/ic_add_black_24dp.png | Bin 0 -> 124 bytes .../ic_arrow_forward_black_24dp.png | Bin 0 -> 147 bytes .../ic_expand_less_black_24dp.png | Bin 0 -> 149 bytes .../ic_expand_more_black_24dp.png | Bin 0 -> 151 bytes .../res/drawable-mdpi/ic_add_black_24dp.png | Bin 0 -> 86 bytes .../ic_arrow_forward_black_24dp.png | Bin 0 -> 114 bytes .../ic_expand_less_black_24dp.png | Bin 0 -> 126 bytes .../ic_expand_more_black_24dp.png | Bin 0 -> 125 bytes .../res/drawable-xhdpi/ic_add_black_24dp.png | Bin 0 -> 108 bytes .../ic_arrow_forward_black_24dp.png | Bin 0 -> 150 bytes .../ic_expand_less_black_24dp.png | Bin 0 -> 171 bytes .../ic_expand_more_black_24dp.png | Bin 0 -> 168 bytes .../res/drawable-xxhdpi/ic_add_black_24dp.png | Bin 0 -> 114 bytes .../ic_arrow_forward_black_24dp.png | Bin 0 -> 186 bytes .../ic_expand_less_black_24dp.png | Bin 0 -> 213 bytes .../ic_expand_more_black_24dp.png | Bin 0 -> 215 bytes .../drawable-xxxhdpi/ic_add_black_24dp.png | Bin 0 -> 119 bytes .../ic_arrow_forward_black_24dp.png | Bin 0 -> 222 bytes .../ic_expand_less_black_24dp.png | Bin 0 -> 261 bytes .../ic_expand_more_black_24dp.png | Bin 0 -> 256 bytes .../src/main/res/layout/cell_saved_board.xml | 42 +++ Clover/app/src/main/res/layout/cell_site.xml | 41 ++ .../res/layout/controller_board_setup.xml | 83 ++++ .../main/res/layout/controller_site_setup.xml | 150 ++++++++ .../res/layout/layout_site_board_select.xml | 2 +- Clover/app/src/main/res/values/strings.xml | 12 + Clover/build.gradle | 3 + Clover/gradle.properties | 1 + 60 files changed, 1366 insertions(+), 171 deletions(-) create mode 100644 Clover/app/src/main/assets/icons/4chan.png create mode 100644 Clover/app/src/main/java/org/floens/chan/core/database/DatabaseSiteManager.java delete mode 100644 Clover/app/src/main/java/org/floens/chan/core/di/ChanGraph.java create mode 100644 Clover/app/src/main/java/org/floens/chan/core/manager/SiteManager.java create mode 100644 Clover/app/src/main/java/org/floens/chan/core/model/SiteModel.java create mode 100644 Clover/app/src/main/java/org/floens/chan/core/presenter/SetupPresenter.java create mode 100644 Clover/app/src/main/java/org/floens/chan/core/site/SiteIcon.java rename Clover/app/src/main/java/org/floens/chan/{utils => ui/animation}/AnimationUtils.java (83%) create mode 100644 Clover/app/src/main/java/org/floens/chan/ui/controller/BoardSetupController.java create mode 100644 Clover/app/src/main/java/org/floens/chan/ui/controller/SiteSetupController.java create mode 100644 Clover/app/src/main/res/drawable-hdpi/ic_add_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-hdpi/ic_arrow_forward_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-hdpi/ic_expand_less_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-hdpi/ic_expand_more_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-mdpi/ic_add_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-mdpi/ic_arrow_forward_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-mdpi/ic_expand_less_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-mdpi/ic_expand_more_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xhdpi/ic_add_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xhdpi/ic_arrow_forward_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xhdpi/ic_expand_less_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xhdpi/ic_expand_more_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xxhdpi/ic_add_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xxhdpi/ic_arrow_forward_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xxhdpi/ic_expand_less_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xxhdpi/ic_expand_more_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xxxhdpi/ic_add_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xxxhdpi/ic_arrow_forward_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xxxhdpi/ic_expand_less_black_24dp.png create mode 100644 Clover/app/src/main/res/drawable-xxxhdpi/ic_expand_more_black_24dp.png create mode 100644 Clover/app/src/main/res/layout/cell_saved_board.xml create mode 100644 Clover/app/src/main/res/layout/cell_site.xml create mode 100644 Clover/app/src/main/res/layout/controller_board_setup.xml create mode 100644 Clover/app/src/main/res/layout/controller_site_setup.xml create mode 100644 Clover/gradle.properties diff --git a/Clover/app/build.gradle b/Clover/app/build.gradle index 2946d886..2919ac3d 100644 --- a/Clover/app/build.gradle +++ b/Clover/app/build.gradle @@ -15,7 +15,7 @@ def getCommitHash = { -> android { compileSdkVersion 25 // update the travis config when changing this - buildToolsVersion '25.0.2' + buildToolsVersion '25.0.3' defaultConfig { minSdkVersion 15 @@ -32,6 +32,11 @@ android { targetCompatibility JavaVersion.VERSION_1_7 } + dexOptions { + preDexLibraries true + maxProcessCount 8 + } + lintOptions { abortOnError false } @@ -122,6 +127,7 @@ dependencies { compile 'com.android.support:support-annotations:25.3.1' compile 'com.android.support:design:25.3.1' compile 'com.android.support:customtabs:25.3.1' + compile 'com.android.support.constraint:constraint-layout:1.0.2' compile 'org.jsoup:jsoup:1.9.2' compile 'com.j256.ormlite:ormlite-core:4.48' @@ -130,9 +136,14 @@ dependencies { compile 'com.davemorrissey.labs:subsampling-scale-image-view:3.5.0' compile 'com.squareup.okhttp3:okhttp:3.4.1' compile 'de.greenrobot:eventbus:2.4.0' - - compile 'com.google.dagger:dagger:2.2' - annotationProcessor 'com.google.dagger:dagger-compiler:2.2' - compile 'org.nibor.autolink:autolink:0.6.0' + + // Yes that's dagger 1, "deprecated". + // There's little wrong with it, except that it might be slow at runtime. + // We don't have a huge graph. The major downside of the newer dagger version + // is that it uses annotation processing. Annotation processors block incremental + // compiling, so our builds become way slower, and I rather have faster builds. + // Move to gradle 2 when incremental compiling supports annotation processors. + compile 'com.squareup.dagger:dagger:1.2.5' + compile 'com.squareup.dagger:dagger-compiler:1.2.5' } diff --git a/Clover/app/src/main/assets/icons/4chan.png b/Clover/app/src/main/assets/icons/4chan.png new file mode 100644 index 0000000000000000000000000000000000000000..dec7c4b6733ae08c0e42f213deac4676c2c1b57e GIT binary patch literal 344 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!93?!50ihlx9Ea{HEjtmSN`?>!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8Uop6XFV_fgtUS@&Et- zRU6lb0Xd8%L4Lsu4$p3+fjCLt?k>!NJU$@X*h@TpUD+S93yNwn*qv9t2NbgLba4#f zxSpKQz#PVYjDc+>XO6eTjcFdf2@|4}wzf2yHkdXraSVDp| zLgoM$!|dH$CtGId8~~c5TH+c}l9E`GYL#4+3Zxi}3=GY64a{{7%|na~txSxp3{15R nfTTgZN`ohghTQy=%(O}@8Vn#BY8=HuVaMR<>gTe~DWM4fsGL|# literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/java/org/floens/chan/Chan.java b/Clover/app/src/main/java/org/floens/chan/Chan.java index debd1a30..479b6967 100644 --- a/Clover/app/src/main/java/org/floens/chan/Chan.java +++ b/Clover/app/src/main/java/org/floens/chan/Chan.java @@ -23,11 +23,10 @@ import android.content.pm.PackageManager; import android.os.Build; import android.os.StrictMode; -import org.floens.chan.core.di.UserAgentProvider; import org.floens.chan.core.database.DatabaseManager; import org.floens.chan.core.di.AppModule; -import org.floens.chan.core.di.ChanGraph; -import org.floens.chan.core.di.DaggerChanGraph; +import org.floens.chan.core.di.NetModule; +import org.floens.chan.core.di.UserAgentProvider; import org.floens.chan.utils.AndroidUtils; import org.floens.chan.utils.Logger; import org.floens.chan.utils.Time; @@ -36,6 +35,7 @@ import java.util.Locale; import javax.inject.Inject; +import dagger.ObjectGraph; import de.greenrobot.event.EventBus; public class Chan extends Application implements UserAgentProvider { @@ -47,7 +47,7 @@ public class Chan extends Application implements UserAgentProvider { private String userAgent; private int activityForegroundCounter = 0; - protected ChanGraph graph; + protected ObjectGraph graph; @Inject DatabaseManager databaseManager; @@ -60,7 +60,7 @@ public class Chan extends Application implements UserAgentProvider { return instance; } - public static ChanGraph getGraph() { + public static ObjectGraph getGraph() { return instance.graph; } @@ -73,9 +73,8 @@ public class Chan extends Application implements UserAgentProvider { AndroidUtils.init(); userAgent = createUserAgent(); - graph = DaggerChanGraph.builder() - .appModule(new AppModule(this, this)) - .build(); + + graph = ObjectGraph.create(new AppModule(this, this), new NetModule()); graph.inject(this); diff --git a/Clover/app/src/main/java/org/floens/chan/chan/ChanHelper.java b/Clover/app/src/main/java/org/floens/chan/chan/ChanHelper.java index f2c1e48e..a43ce75a 100644 --- a/Clover/app/src/main/java/org/floens/chan/chan/ChanHelper.java +++ b/Clover/app/src/main/java/org/floens/chan/chan/ChanHelper.java @@ -19,10 +19,8 @@ package org.floens.chan.chan; import android.net.Uri; -import org.floens.chan.Chan; import org.floens.chan.core.database.DatabaseLoadableManager; import org.floens.chan.core.database.DatabaseManager; -import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.model.Board; import org.floens.chan.core.model.Loadable; import org.floens.chan.core.site.Site; @@ -43,7 +41,7 @@ public class ChanHelper { if (parts.size() > 0) { String rawBoard = parts.get(0); - DatabaseManager databaseManager = getGraph().getDatabaseManager(); + DatabaseManager databaseManager = getGraph().get(DatabaseManager.class); DatabaseLoadableManager loadableManager = databaseManager.getDatabaseLoadableManager(); Board board = site.board(rawBoard); if (board != null) { diff --git a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseHelper.java b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseHelper.java index dc337e42..84088905 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseHelper.java +++ b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseHelper.java @@ -31,6 +31,7 @@ import org.floens.chan.core.model.History; import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Pin; import org.floens.chan.core.model.SavedReply; +import org.floens.chan.core.model.SiteModel; import org.floens.chan.core.model.ThreadHide; import org.floens.chan.utils.Logger; @@ -52,6 +53,7 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper { public Dao threadHideDao; public Dao historyDao; public Dao filterDao; + public Dao siteDao; private final Context context; diff --git a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseManager.java b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseManager.java index fb638053..3d9f0caf 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseManager.java +++ b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseManager.java @@ -73,6 +73,7 @@ public class DatabaseManager { private final DatabaseSavedReplyManager databaseSavedReplyManager; private final DatabaseFilterManager databaseFilterManager; private final DatabaseBoardManager databaseBoardManager; + private final DatabaseSiteManager databaseSiteManager; @Inject public DatabaseManager(Context context) { @@ -85,6 +86,7 @@ public class DatabaseManager { databaseSavedReplyManager = new DatabaseSavedReplyManager(this, helper); databaseFilterManager = new DatabaseFilterManager(this, helper); databaseBoardManager = new DatabaseBoardManager(this, helper); + databaseSiteManager = new DatabaseSiteManager(this, helper); initialize(); EventBus.getDefault().register(this); } @@ -113,6 +115,10 @@ public class DatabaseManager { return databaseBoardManager; } + public DatabaseSiteManager getDatabaseSiteManager() { + return databaseSiteManager; + } + // Called when the app changes foreground state public void onEvent(Chan.ForegroundChangedMessage message) { if (!message.inForeground) { @@ -122,8 +128,12 @@ public class DatabaseManager { private void initialize() { loadThreadHides(); - runTaskSync(databaseHistoryManager.load()); + + // Loads data into fields. runTaskSync(databaseSavedReplyManager.load()); + + // Only trims. + runTask(databaseHistoryManager.load()); } /** diff --git a/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseSiteManager.java b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseSiteManager.java new file mode 100644 index 00000000..af688c40 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/database/DatabaseSiteManager.java @@ -0,0 +1,29 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan.core.database; + + +public class DatabaseSiteManager { + private DatabaseManager databaseManager; + private DatabaseHelper databaseHelper; + + public DatabaseSiteManager(DatabaseManager databaseManager, DatabaseHelper databaseHelper) { + this.databaseManager = databaseManager; + this.databaseHelper = databaseHelper; + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/di/AppModule.java b/Clover/app/src/main/java/org/floens/chan/core/di/AppModule.java index 5db95bb9..885985ca 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/di/AppModule.java +++ b/Clover/app/src/main/java/org/floens/chan/core/di/AppModule.java @@ -5,14 +5,98 @@ import android.content.Context; import com.android.volley.RequestQueue; import com.android.volley.toolbox.ImageLoader; +import org.floens.chan.ChanApplication; +import org.floens.chan.chan.ChanLoader; +import org.floens.chan.chan.ChanParser; +import org.floens.chan.core.cache.FileCache; +import org.floens.chan.core.database.DatabaseManager; +import org.floens.chan.core.manager.BoardManager; +import org.floens.chan.core.manager.FilterEngine; +import org.floens.chan.core.manager.ReplyManager; +import org.floens.chan.core.manager.SiteManager; +import org.floens.chan.core.manager.WatchManager; import org.floens.chan.core.net.BitmapLruImageCache; +import org.floens.chan.core.presenter.ImageViewerPresenter; +import org.floens.chan.core.presenter.ReplyPresenter; +import org.floens.chan.core.presenter.ThreadPresenter; +import org.floens.chan.core.receiver.WatchUpdateReceiver; +import org.floens.chan.core.saver.ImageSaveTask; +import org.floens.chan.core.site.http.HttpCallManager; +import org.floens.chan.core.site.sites.chan4.Chan4; +import org.floens.chan.core.site.sites.chan4.Chan4ReaderRequest; +import org.floens.chan.core.update.UpdateManager; +import org.floens.chan.ui.activity.BoardActivity; +import org.floens.chan.ui.adapter.DrawerAdapter; +import org.floens.chan.ui.adapter.PostsFilter; +import org.floens.chan.ui.controller.BoardEditController; +import org.floens.chan.ui.controller.BrowseController; +import org.floens.chan.ui.controller.DeveloperSettingsController; +import org.floens.chan.ui.controller.DrawerController; +import org.floens.chan.ui.controller.FiltersController; +import org.floens.chan.ui.controller.HistoryController; +import org.floens.chan.ui.controller.ImageViewerController; +import org.floens.chan.ui.controller.MainSettingsController; +import org.floens.chan.ui.controller.PassSettingsController; +import org.floens.chan.ui.controller.ViewThreadController; +import org.floens.chan.ui.helper.ImagePickDelegate; +import org.floens.chan.ui.layout.FilterLayout; +import org.floens.chan.ui.layout.ThreadLayout; +import org.floens.chan.ui.service.WatchNotifier; +import org.floens.chan.ui.view.MultiImageView; import javax.inject.Singleton; import dagger.Module; import dagger.Provides; -@Module +@Module( + injects = { +// Context.class, // ApplicationContext + + ChanParser.class, + BoardManager.class, + DatabaseManager.class, + ReplyManager.class, + ImageLoader.class, + FileCache.class, + HttpCallManager.class, + + ChanApplication.class, + MainSettingsController.class, + ReplyPresenter.class, + Chan4ReaderRequest.class, + ThreadLayout.class, + DeveloperSettingsController.class, + BoardActivity.class, + ThreadPresenter.class, + BoardEditController.class, + FilterEngine.class, + BrowseController.class, + FilterLayout.class, + HistoryController.class, + DrawerController.class, + DrawerAdapter.class, + WatchNotifier.class, + WatchUpdateReceiver.class, + ImagePickDelegate.class, + PassSettingsController.class, + FiltersController.class, + PostsFilter.class, + ChanLoader.class, + ImageViewerController.class, + ImageViewerPresenter.class, + MultiImageView.class, + ImageSaveTask.class, + ViewThreadController.class, + WatchManager.PinWatcher.class, + UpdateManager.class, + SiteManager.class, + + Chan4.class, + }, + complete = false, + includes = NetModule.class +) public class AppModule { private Context applicationContext; private UserAgentProvider userAgentProvider; diff --git a/Clover/app/src/main/java/org/floens/chan/core/di/ChanGraph.java b/Clover/app/src/main/java/org/floens/chan/core/di/ChanGraph.java deleted file mode 100644 index be9d9870..00000000 --- a/Clover/app/src/main/java/org/floens/chan/core/di/ChanGraph.java +++ /dev/null @@ -1,131 +0,0 @@ -package org.floens.chan.core.di; - -import com.android.volley.RequestQueue; -import com.android.volley.toolbox.ImageLoader; - -import org.floens.chan.Chan; -import org.floens.chan.chan.ChanLoader; -import org.floens.chan.chan.ChanParser; -import org.floens.chan.core.cache.FileCache; -import org.floens.chan.core.database.DatabaseManager; -import org.floens.chan.core.manager.BoardManager; -import org.floens.chan.core.manager.FilterEngine; -import org.floens.chan.core.manager.ReplyManager; -import org.floens.chan.core.manager.WatchManager; -import org.floens.chan.core.presenter.ImageViewerPresenter; -import org.floens.chan.core.presenter.ReplyPresenter; -import org.floens.chan.core.presenter.ThreadPresenter; -import org.floens.chan.core.receiver.WatchUpdateReceiver; -import org.floens.chan.core.saver.ImageSaveTask; -import org.floens.chan.core.site.http.HttpCallManager; -import org.floens.chan.core.site.sites.chan4.Chan4; -import org.floens.chan.core.site.sites.chan4.Chan4ReaderRequest; -import org.floens.chan.core.update.UpdateManager; -import org.floens.chan.ui.activity.StartActivity; -import org.floens.chan.ui.adapter.DrawerAdapter; -import org.floens.chan.ui.adapter.PostsFilter; -import org.floens.chan.ui.controller.BoardEditController; -import org.floens.chan.ui.controller.BrowseController; -import org.floens.chan.ui.controller.DeveloperSettingsController; -import org.floens.chan.ui.controller.DrawerController; -import org.floens.chan.ui.controller.FiltersController; -import org.floens.chan.ui.controller.HistoryController; -import org.floens.chan.ui.controller.ImageViewerController; -import org.floens.chan.ui.controller.MainSettingsController; -import org.floens.chan.ui.controller.PassSettingsController; -import org.floens.chan.ui.controller.ViewThreadController; -import org.floens.chan.ui.helper.ImagePickDelegate; -import org.floens.chan.ui.layout.FilterLayout; -import org.floens.chan.ui.layout.ThreadLayout; -import org.floens.chan.ui.service.WatchNotifier; -import org.floens.chan.ui.view.MultiImageView; - -import javax.inject.Singleton; - -import dagger.Component; - -@Component(modules = { - AppModule.class, - NetModule.class -}) -@Singleton -/** - * Note: please avoid adding inject() statements for Sites. - */ -public interface ChanGraph { - ChanParser getChanParser(); - - BoardManager getBoardManager(); - - DatabaseManager getDatabaseManager(); - - ReplyManager getReplyManager(); - - RequestQueue getRequestQueue(); - - ImageLoader getImageLoader(); - - FileCache getFileCache(); - - HttpCallManager getHttpCallManager(); - - void inject(Chan chan); - - void inject(MainSettingsController mainSettingsController); - - void inject(ReplyPresenter replyPresenter); - - void inject(Chan4ReaderRequest chanReaderRequest); - - void inject(ThreadLayout threadLayout); - - void inject(DeveloperSettingsController developerSettingsController); - - void inject(StartActivity startActivity); - - void inject(ThreadPresenter threadPresenter); - - void inject(BoardEditController boardEditController); - - void inject(FilterEngine filterEngine); - - void inject(BrowseController browseController); - - void inject(FilterLayout filterLayout); - - void inject(HistoryController historyController); - - void inject(DrawerController drawerController); - - void inject(DrawerAdapter drawerAdapter); - - void inject(WatchNotifier watchNotifier); - - void inject(WatchUpdateReceiver watchUpdateReceiver); - - void inject(ImagePickDelegate imagePickDelegate); - - void inject(PassSettingsController passSettingsController); - - void inject(FiltersController filtersController); - - void inject(PostsFilter postsFilter); - - void inject(ChanLoader chanLoader); - - void inject(ImageViewerController imageViewerController); - - void inject(ImageViewerPresenter imageViewerPresenter); - - void inject(MultiImageView multiImageView); - - void inject(ImageSaveTask imageSaveTask); - - void inject(ViewThreadController viewThreadController); - - void inject(WatchManager.PinWatcher pinWatcher); - - void inject(UpdateManager updateManager); - - void inject(Chan4 chan4); -} diff --git a/Clover/app/src/main/java/org/floens/chan/core/di/NetModule.java b/Clover/app/src/main/java/org/floens/chan/core/di/NetModule.java index 5f57ea2d..8ac1e4cf 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/di/NetModule.java +++ b/Clover/app/src/main/java/org/floens/chan/core/di/NetModule.java @@ -15,7 +15,13 @@ import javax.inject.Singleton; import dagger.Module; import dagger.Provides; -@Module +@Module( + injects = { + RequestQueue.class, + FileCache.class + }, + complete = false +) public class NetModule { private static final int VOLLEY_CACHE_SIZE = 10 * 1024 * 1024; private static final long FILE_CACHE_DISK_SIZE = 50 * 1024 * 1024; diff --git a/Clover/app/src/main/java/org/floens/chan/core/manager/SiteManager.java b/Clover/app/src/main/java/org/floens/chan/core/manager/SiteManager.java new file mode 100644 index 00000000..b7c5b7d9 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/manager/SiteManager.java @@ -0,0 +1,29 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan.core.manager; + + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class SiteManager { + @Inject + public SiteManager() { + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/manager/WatchManager.java b/Clover/app/src/main/java/org/floens/chan/core/manager/WatchManager.java index e781e821..43ca7fb9 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/manager/WatchManager.java +++ b/Clover/app/src/main/java/org/floens/chan/core/manager/WatchManager.java @@ -113,6 +113,9 @@ public class WatchManager { } }; + @Inject + ChanLoaderFactory chanLoaderFactory; + private final AlarmManager alarmManager; private final PowerManager powerManager; @@ -651,9 +654,6 @@ public class WatchManager { public class PinWatcher implements ChanLoader.ChanLoaderCallback { private static final String TAG = "PinWatcher"; - @Inject - ChanLoaderFactory chanLoaderFactory; - private final Pin pin; private ChanLoader chanLoader; diff --git a/Clover/app/src/main/java/org/floens/chan/core/model/SiteModel.java b/Clover/app/src/main/java/org/floens/chan/core/model/SiteModel.java new file mode 100644 index 00000000..bf08005f --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/model/SiteModel.java @@ -0,0 +1,37 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan.core.model; + +import com.j256.ormlite.field.DatabaseField; +import com.j256.ormlite.table.DatabaseTable; + +/** + * Only for storing what sites we know of, for hardcoded sites the sites we have enabled, + * and for dynamic sites all settings for it. + */ +@DatabaseTable +public class SiteModel { + @DatabaseField(id = true, unique = true) + public int id; + + @DatabaseField + public String name; + + @DatabaseField + public String options; +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/presenter/SetupPresenter.java b/Clover/app/src/main/java/org/floens/chan/core/presenter/SetupPresenter.java new file mode 100644 index 00000000..bf69e257 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/presenter/SetupPresenter.java @@ -0,0 +1,120 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan.core.presenter; + + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; + +import org.floens.chan.utils.AndroidUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.floens.chan.utils.AndroidUtils.getAppContext; +import static org.floens.chan.utils.AndroidUtils.getRes; + +public class SetupPresenter { + private Callback callback; + + private List sites = new ArrayList<>(); + + public void create(Callback callback) { + this.callback = callback; + + this.callback.setAddedSites(sites); + + this.callback.setNextAllowed(!sites.isEmpty(), false); + } + + public boolean mayExit() { + return false; + } + + public void onUrlSubmitClicked(String url) { + callback.goToUrlSubmittedState(); + + AndroidUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + siteAdded(getTestSite()); + } + }, 500); +// callback.showUrlHint("foo bar baz"); + } + + public void onNextClicked() { + if (!sites.isEmpty()) { + callback.moveToSavedBoards(); + } + } + + private void siteAdded(AddedSite site) { + sites.add(site); + callback.setAddedSites(sites); + callback.runSiteAddedAnimation(site); + + callback.setNextAllowed(!sites.isEmpty(), true); + } + + private int counter; + + private AddedSite getTestSite() { + AddedSite site = new AddedSite(); + site.id = counter++; + site.title = "4chan.org"; + + Bitmap bitmap; + try { + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inScaled = false; + bitmap = BitmapFactory.decodeStream(getAppContext().getAssets().open("icons/4chan.png"), null, opts); + } catch (IOException e) { + throw new RuntimeException(e); + } + + BitmapDrawable drawable = new BitmapDrawable(getRes(), bitmap); + drawable = (BitmapDrawable) drawable.mutate(); + drawable.getPaint().setFilterBitmap(false); + site.drawable = drawable; + return site; + } + + public interface Callback { + void goToUrlSubmittedState(); + + void runSiteAddedAnimation(AddedSite site); + + void setAddedSites(List sites); + + void setNextAllowed(boolean nextAllowed, boolean animate); + + void showUrlHint(String text); + + void moveToSavedBoards(); + } + + public static class AddedSite { + public int id; + public String title; + public Drawable drawable; + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/Site.java b/Clover/app/src/main/java/org/floens/chan/core/site/Site.java index 5a6b23a3..71e73ccb 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/site/Site.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/Site.java @@ -111,6 +111,8 @@ public interface Site { String name(); + SiteIcon icon(); + boolean feature(Feature feature); boolean boardFeature(BoardFeature boardFeature, Board board); diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/SiteIcon.java b/Clover/app/src/main/java/org/floens/chan/core/site/SiteIcon.java new file mode 100644 index 00000000..d6f8547b --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/core/site/SiteIcon.java @@ -0,0 +1,72 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan.core.site; + + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; + +import java.io.IOException; + +import okhttp3.HttpUrl; + +import static org.floens.chan.utils.AndroidUtils.getAppContext; +import static org.floens.chan.utils.AndroidUtils.getRes; + +public class SiteIcon { + private String assetPath; + private HttpUrl url; + + public static SiteIcon fromAssets(String path) { + SiteIcon siteIcon = new SiteIcon(); + siteIcon.assetPath = path; + return siteIcon; + } + + public static SiteIcon fromUrl(HttpUrl url) { + SiteIcon siteIcon = new SiteIcon(); + siteIcon.url = url; + return siteIcon; + } + + public void get(SiteIconResult result) { + if (assetPath != null) { + Bitmap bitmap; + try { + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inScaled = false; + bitmap = BitmapFactory.decodeStream(getAppContext().getAssets().open(assetPath), null, opts); + } catch (IOException e) { + throw new RuntimeException(e); + } + + BitmapDrawable drawable = new BitmapDrawable(getRes(), bitmap); + drawable = (BitmapDrawable) drawable.mutate(); + drawable.getPaint().setFilterBitmap(false); + result.onSiteIcon(this, drawable); + } else if (url != null) { + // TODO + } + } + + public interface SiteIconResult { + void onSiteIcon(SiteIcon siteIcon, Drawable icon); + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java index 7a6c22d8..c8d84bda 100644 --- a/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java +++ b/Clover/app/src/main/java/org/floens/chan/core/site/sites/chan4/Chan4.java @@ -28,6 +28,7 @@ import com.android.volley.VolleyError; import org.floens.chan.chan.ChanLoaderRequest; import org.floens.chan.chan.ChanLoaderRequestParams; +import org.floens.chan.core.manager.BoardManager; import org.floens.chan.core.model.Board; import org.floens.chan.core.model.Loadable; import org.floens.chan.core.model.Post; @@ -36,6 +37,7 @@ import org.floens.chan.core.site.Boards; import org.floens.chan.core.site.Site; import org.floens.chan.core.site.SiteAuthentication; import org.floens.chan.core.site.SiteEndpoints; +import org.floens.chan.core.site.SiteIcon; import org.floens.chan.core.site.SiteRequestModifier; import org.floens.chan.core.site.http.DeleteRequest; import org.floens.chan.core.site.http.HttpCall; @@ -279,6 +281,11 @@ public class Chan4 implements Site { return "4chan"; } + @Override + public SiteIcon icon() { + return SiteIcon.fromAssets("icons/4chan.png"); + } + @Override public boolean feature(Feature feature) { switch (feature) { @@ -360,7 +367,7 @@ public class Chan4 implements Site { @Override public Board board(String code) { - List allBoards = getGraph().getBoardManager().getAllBoards(); + List allBoards = getGraph().get(BoardManager.class).getAllBoards(); for (Board board : allBoards) { if (board.code.equals(code)) { return board; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java b/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java index 89a1fed0..15ec5346 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/activity/StartActivity.java @@ -51,6 +51,7 @@ import org.floens.chan.core.site.Sites; import org.floens.chan.ui.controller.BrowseController; import org.floens.chan.ui.controller.DoubleNavigationController; import org.floens.chan.ui.controller.DrawerController; +import org.floens.chan.ui.controller.SiteSetupController; import org.floens.chan.ui.controller.SplitNavigationController; import org.floens.chan.ui.controller.StyledToolbarNavigationController; import org.floens.chan.ui.controller.ThreadSlideController; @@ -127,9 +128,15 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat adapter.setNdefPushMessageCallback(this, this); } - // Startup from background or url + setupFromStateOrFreshLaunch(savedInstanceState); + + versionHandler.run(); + } + + private void setupFromStateOrFreshLaunch(Bundle savedInstanceState) { boolean loadDefault = true; if (savedInstanceState != null) { + // Restore the activity state from the previously saved state. ChanState chanState = savedInstanceState.getParcelable(STATE_KEY); if (chanState == null) { Logger.w(TAG, "savedInstanceState was not null, but no ChanState was found!"); @@ -148,6 +155,7 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat } } else { final Uri data = getIntent().getData(); + // Start from an url launch. if (data != null) { Loadable fromUri = ChanHelper.getLoadableFromStartUri(data); if (fromUri != null) { @@ -171,11 +179,15 @@ public class StartActivity extends AppCompatActivity implements NfcAdapter.Creat } } + // Not from a state or from an url, launch the setup controller if no boards are setup up yet, + // otherwise load the default saved board. if (loadDefault) { - browseController.loadDefault(); + if (boardManager.getSavedBoards().isEmpty()) { + mainNavigationController.pushController(new SiteSetupController(this), false); + } else { + browseController.loadDefault(); + } } - - versionHandler.run(); } private Pair resolveChanState(ChanState state) { diff --git a/Clover/app/src/main/java/org/floens/chan/utils/AnimationUtils.java b/Clover/app/src/main/java/org/floens/chan/ui/animation/AnimationUtils.java similarity index 83% rename from Clover/app/src/main/java/org/floens/chan/utils/AnimationUtils.java rename to Clover/app/src/main/java/org/floens/chan/ui/animation/AnimationUtils.java index 77484d73..e33c3a14 100644 --- a/Clover/app/src/main/java/org/floens/chan/utils/AnimationUtils.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/animation/AnimationUtils.java @@ -15,14 +15,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.floens.chan.utils; +package org.floens.chan.ui.animation; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.ArgbEvaluator; import android.animation.ValueAnimator; +import android.graphics.drawable.ColorDrawable; import android.view.View; import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; +import android.widget.TextView; import java.util.HashMap; import java.util.Map; @@ -153,4 +156,27 @@ public class AnimationUtils { public interface LayoutAnimationProgress { void onLayoutAnimationProgress(View view, boolean vertical, int from, int to, int value, float progress); } + + public static void animateTextColor(final TextView text, int to) { + ValueAnimator animation = ValueAnimator.ofObject(new ArgbEvaluator(), text.getCurrentTextColor(), to); + animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + text.setTextColor((int) animation.getAnimatedValue()); + } + }); + animation.start(); + } + + public static void animateBackgroundColorDrawable(final View view, int newColor) { + int currentBackground = ((ColorDrawable) view.getBackground()).getColor(); + ValueAnimator animation = ValueAnimator.ofObject(new ArgbEvaluator(), currentBackground, newColor); + animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + view.setBackgroundColor((int) animation.getAnimatedValue()); + } + }); + animation.start(); + } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/cell/PostCell.java b/Clover/app/src/main/java/org/floens/chan/ui/cell/PostCell.java index aeb63af3..9a98288d 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/cell/PostCell.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/cell/PostCell.java @@ -777,7 +777,7 @@ public class PostCell extends LinearLayout implements PostCellInterface { } private void request() { - request = getGraph().getImageLoader().get(url.toString(), this); + request = getGraph().get(ImageLoader.class).get(url.toString(), this); } private void cancel() { diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardSetupController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardSetupController.java new file mode 100644 index 00000000..6cc1d080 --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/BoardSetupController.java @@ -0,0 +1,178 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan.ui.controller; + + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.drawable.BitmapDrawable; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.AutoCompleteTextView; +import android.widget.ImageView; +import android.widget.TextView; + +import org.floens.chan.R; +import org.floens.chan.controller.Controller; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.floens.chan.utils.AndroidUtils.getAppContext; +import static org.floens.chan.utils.AndroidUtils.getRes; + +public class BoardSetupController extends Controller implements View.OnClickListener { + private AutoCompleteTextView code; + private RecyclerView savedBoardsRecycler; + + private SavedBoardsAdapter adapter; + + public BoardSetupController(Context context) { + super(context); + } + + @Override + public void onCreate() { + super.onCreate(); + + view = inflateRes(R.layout.controller_board_setup); + navigationItem.setTitle(R.string.saved_boards_title); + + code = (AutoCompleteTextView) view.findViewById(R.id.code); + savedBoardsRecycler = (RecyclerView) view.findViewById(R.id.boards_recycler); + savedBoardsRecycler.setLayoutManager(new LinearLayoutManager(context)); + + adapter = new SavedBoardsAdapter(); + savedBoardsRecycler.setAdapter(adapter); + + List savedBoards = new ArrayList<>(); + for (int board = 0; board < 5; board++) { + savedBoards.add(new SavedBoard("foo - " + board, board)); + } + + adapter.setSavedBoards(savedBoards); + + List foo = new ArrayList() {{ + add("foo"); + add("foo"); + add("foo"); + add("foo"); + add("foo"); + add("foo"); + add("foo"); + add("foo"); + add("foo"); + add("foo"); + add("foo"); + add("foo"); + add("foo"); + add("foo"); + add("bar"); + add("baz"); + }}; + + ArrayAdapter adapter = new ArrayAdapter<>(context, android.R.layout.simple_dropdown_item_1line, foo); + code.setAdapter(adapter); + code.setThreshold(1); + } + + @Override + public void onClick(View v) { + } + + private class SavedBoard { + private String title; + private int id; + + public SavedBoard(String title, int id) { + this.title = title; + this.id = id; + } + } + + private class SavedBoardsAdapter extends RecyclerView.Adapter { + private List savedBoards = new ArrayList<>(); + + public SavedBoardsAdapter() { + setHasStableIds(true); + } + + private void setSavedBoards(List savedBoards) { + this.savedBoards.clear(); + this.savedBoards.addAll(savedBoards); + notifyDataSetChanged(); + } + + @Override + public long getItemId(int position) { + return savedBoards.get(position).id; + } + + @Override + public SavedBoardCell onCreateViewHolder(ViewGroup parent, int viewType) { + return new SavedBoardCell(LayoutInflater.from(context).inflate(R.layout.cell_saved_board, parent, false)); + } + + @Override + public void onBindViewHolder(SavedBoardCell holder, int position) { + SavedBoard savedBoard = savedBoards.get(position); + holder.setSavedBoard(savedBoard); + } + + @Override + public int getItemCount() { + return savedBoards.size(); + } + } + + private class SavedBoardCell extends RecyclerView.ViewHolder { + private ImageView image; + private TextView text; + + public SavedBoardCell(View itemView) { + super(itemView); + + image = (ImageView) itemView.findViewById(R.id.image); + text = (TextView) itemView.findViewById(R.id.text); + } + + public void setSavedBoard(SavedBoard savedBoard) { + Bitmap bitmap; + try { + BitmapFactory.Options opts = new BitmapFactory.Options(); + opts.inScaled = false; + bitmap = BitmapFactory.decodeStream(getAppContext().getAssets().open("icons/4chan.png"), null, opts); + } catch (IOException e) { + throw new RuntimeException(e); + } + + BitmapDrawable drawable = new BitmapDrawable(getRes(), bitmap); + drawable = (BitmapDrawable) drawable.mutate(); + drawable.getPaint().setFilterBitmap(false); + + image.setImageDrawable(drawable); + text.setText(savedBoard.title); + } + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java index 39bbddb4..7c0b0bad 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/MainSettingsController.java @@ -48,7 +48,7 @@ import org.floens.chan.ui.toolbar.ToolbarMenu; import org.floens.chan.ui.toolbar.ToolbarMenuItem; import org.floens.chan.ui.view.FloatingMenuItem; import org.floens.chan.utils.AndroidUtils; -import org.floens.chan.utils.AnimationUtils; +import org.floens.chan.ui.animation.AnimationUtils; import java.util.ArrayList; import java.util.Collections; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java index 395c8993..87ca474a 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/PassSettingsController.java @@ -37,7 +37,7 @@ import org.floens.chan.core.site.http.LoginRequest; import org.floens.chan.core.site.http.LoginResponse; import org.floens.chan.ui.view.CrossfadeView; import org.floens.chan.utils.AndroidUtils; -import org.floens.chan.utils.AnimationUtils; +import org.floens.chan.ui.animation.AnimationUtils; import javax.inject.Inject; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/SiteSetupController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/SiteSetupController.java new file mode 100644 index 00000000..7367bc5e --- /dev/null +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/SiteSetupController.java @@ -0,0 +1,357 @@ +/* + * Clover - 4chan browser https://github.com/Floens/Clover/ + * Copyright (C) 2014 Floens + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.floens.chan.ui.controller; + + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ArgbEvaluator; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.os.Build; +import android.support.v4.view.animation.PathInterpolatorCompat; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewGroupOverlay; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; + +import org.floens.chan.R; +import org.floens.chan.controller.transition.FadeInTransition; +import org.floens.chan.core.presenter.SetupPresenter; +import org.floens.chan.ui.animation.AnimationUtils; + +import java.util.ArrayList; +import java.util.List; + +import static org.floens.chan.utils.AndroidUtils.dp; +import static org.floens.chan.utils.AndroidUtils.getAttrColor; + +public class SiteSetupController extends StyledToolbarNavigationController implements View.OnClickListener, SetupPresenter.Callback { + private EditText url; + private View urlSubmit; + private View spinner; + private Button next; + + private boolean blocked = false; + + private SetupPresenter presenter; + + private RecyclerView sitesRecyclerview; + private SitesAdapter sitesAdapter; + private List sites = new ArrayList<>(); + + public SiteSetupController(Context context) { + super(context); + } + + @Override + public void onCreate() { + super.onCreate(); + + view = inflateRes(R.layout.controller_site_setup); + navigationItem.setTitle(R.string.setup_title); + + url = (EditText) view.findViewById(R.id.site_url); + urlSubmit = view.findViewById(R.id.site_url_submit); + urlSubmit.setOnClickListener(this); + spinner = view.findViewById(R.id.progress); + sitesRecyclerview = (RecyclerView) view.findViewById(R.id.sites_recycler); + sitesRecyclerview.setLayoutManager(new LinearLayoutManager(context)); + next = (Button) view.findViewById(R.id.next_button); + next.setOnClickListener(this); + + presenter = new SetupPresenter(); + + sitesAdapter = new SitesAdapter(); + sitesRecyclerview.setAdapter(sitesAdapter); + + presenter.create(this); + } + + @Override + public void onClick(View v) { + if (blocked) return; + + if (v == urlSubmit) { + presenter.onUrlSubmitClicked(url.getText().toString()); + } else if (v == next) { + presenter.onNextClicked(); + } + } + + @Override + public boolean onBack() { + if (presenter.mayExit()) { + return super.onBack(); + } else { + return true; + } + } + + @Override + public void moveToSavedBoards() { + navigationController.pushController(new BoardSetupController(context), new FadeInTransition()); + } + + @Override + public void goToUrlSubmittedState() { + spinner.setVisibility(View.VISIBLE); + urlSubmit.setVisibility(View.INVISIBLE); + } + + @Override + public void runSiteAddedAnimation(final SetupPresenter.AddedSite site) { + spinner.setVisibility(View.INVISIBLE); + urlSubmit.setVisibility(View.VISIBLE); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + runSiteAddedAnimationInternal(site); + } else { + resetUrl(); + } + } + + @Override + public void showUrlHint(String text) { + spinner.setVisibility(View.INVISIBLE); + urlSubmit.setVisibility(View.VISIBLE); + + url.setError(text, null); + } + + @Override + public void setAddedSites(List sites) { + this.sites.clear(); + this.sites.addAll(sites); + sitesAdapter.notifyDataSetChanged(); + } + + @Override + public void setNextAllowed(boolean nextAllowed, boolean animate) { + next.setEnabled(nextAllowed); + int newBackground = getAttrColor(context, nextAllowed ? R.attr.colorAccent : R.attr.backcolor); + int newTextColor = nextAllowed ? Color.WHITE : getAttrColor(context, R.attr.text_color_hint); + if (animate) { + AnimationUtils.animateTextColor(next, newTextColor); + AnimationUtils.animateBackgroundColorDrawable(next, newBackground); + } else { + next.setBackgroundColor(newBackground); + next.setTextColor(newTextColor); + } + } + + private void resetUrl() { + url.setText(""); + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) + private void runSiteAddedAnimationInternal(SetupPresenter.AddedSite site) { + blocked = true; + sitesAdapter.invisibleSiteOnBind = site; + + SiteCell siteCell = new SiteCell(LayoutInflater.from(context).inflate(R.layout.cell_site, null)); + siteCell.setSite(site); + final View siteCellView = siteCell.itemView; + final View siteCellIcon = siteCell.image; + siteCellView.measure(View.MeasureSpec.makeMeasureSpec(url.getMeasuredWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); + siteCellView.layout(0, 0, siteCellView.getMeasuredWidth(), siteCellView.getMeasuredHeight()); + + final ViewGroupOverlay overlay = view.getOverlay(); + + overlay.add(siteCellView); + + int[] urlWinLoc = new int[2]; + url.getLocationInWindow(urlWinLoc); + int[] recWinLoc = new int[2]; + sitesRecyclerview.getLocationInWindow(recWinLoc); + recWinLoc[0] += sitesRecyclerview.getPaddingLeft(); + recWinLoc[1] += sitesRecyclerview.getPaddingTop() + siteCellView.getMeasuredHeight() * (sites.size() - 1); + int[] viewWinLoc = new int[2]; + view.getLocationInWindow(viewWinLoc); + + String urlText = url.getText().toString(); + int indexOf = urlText.indexOf(site.title); + int offsetLeft = 0; + if (indexOf > 0) { + Paint paint = new Paint(); + paint.setTextSize(url.getTextSize()); + offsetLeft = (int) paint.measureText(urlText.substring(0, indexOf)); + } + + final int staX = urlWinLoc[0] - viewWinLoc[0] - dp(48) + dp(4) + offsetLeft; + final int staY = urlWinLoc[1] - viewWinLoc[1] - dp(4); + final int desX = recWinLoc[0] - viewWinLoc[0]; + final int desY = recWinLoc[1] - viewWinLoc[1]; + + siteCellView.setTranslationX(staX); + siteCellView.setTranslationY(staY); + + final int textColor = url.getCurrentTextColor(); + final int textHintColor = url.getCurrentHintTextColor(); + + ValueAnimator textAlpha = ValueAnimator.ofObject(new ArgbEvaluator(), + textColor, textColor & 0xffffff); + textAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + url.setTextColor((int) animation.getAnimatedValue()); + } + }); + textAlpha.setDuration(300); + textAlpha.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + resetUrl(); + } + }); + + url.setCursorVisible(false); + url.setHintTextColor(0); + + ValueAnimator alpha = ObjectAnimator.ofFloat(siteCellView, View.ALPHA, 0f, 1f); + alpha.setDuration(300); + + ValueAnimator iconAlpha = ValueAnimator.ofFloat(0f, 1f); + iconAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + siteCellIcon.setAlpha((float) animation.getAnimatedValue()); + } + }); + iconAlpha.setDuration(400); + iconAlpha.setStartDelay(500); + siteCellIcon.setAlpha(0f); + + ValueAnimator horizontal = ValueAnimator.ofFloat(0f, 1f); + horizontal.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float value = (float) animation.getAnimatedValue(); + siteCellView.setTranslationX(staX + (desX - staX) * value); + } + }); + horizontal.setDuration(600); + horizontal.setStartDelay(400); + + ValueAnimator vertical = ValueAnimator.ofFloat(0f, 1f); + vertical.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float value = (float) animation.getAnimatedValue(); + siteCellView.setTranslationY(staY + (desY - staY) * value); + } + }); + + Path path = new Path(); + + float jump = 300f / Math.abs(desY - staY); + path.cubicTo(0.5f, 0f - jump, 0.75f, 1.0f, 1f, 1f); + vertical.setInterpolator(PathInterpolatorCompat.create(path)); + vertical.setDuration(600); + vertical.setStartDelay(400); + + ValueAnimator hintAlpha = ValueAnimator.ofObject(new ArgbEvaluator(), + textHintColor & 0xffffff, textHintColor); + hintAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + url.setHintTextColor((int) animation.getAnimatedValue()); + } + }); + hintAlpha.setDuration(300); + hintAlpha.setStartDelay(1000); + + AnimatorSet set = new AnimatorSet(); + set.playTogether(alpha, textAlpha, iconAlpha, horizontal, vertical, hintAlpha); + set.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + blocked = false; + overlay.remove(siteCellView); + sitesAdapter.invisibleSiteOnBind = null; + for (int i = 0; i < sitesRecyclerview.getChildCount(); i++) { + sitesRecyclerview.getChildAt(i).setVisibility(View.VISIBLE); + } + url.setTextColor(textColor); + url.setHintTextColor(textHintColor); + url.setCursorVisible(true); + } + }); + set.start(); + } + + private class SitesAdapter extends RecyclerView.Adapter { + private SetupPresenter.AddedSite invisibleSiteOnBind; + + public SitesAdapter() { + setHasStableIds(true); + } + + @Override + public long getItemId(int position) { + return sites.get(position).id; + } + + @Override + public SiteCell onCreateViewHolder(ViewGroup parent, int viewType) { + return new SiteCell(LayoutInflater.from(context).inflate(R.layout.cell_site, parent, false)); + } + + @Override + public void onBindViewHolder(SiteCell holder, int position) { + SetupPresenter.AddedSite site = sites.get(position); + holder.setSite(site); + if (site == invisibleSiteOnBind) { + holder.itemView.setVisibility(View.INVISIBLE); + } + } + + @Override + public int getItemCount() { + return sites.size(); + } + } + + private class SiteCell extends RecyclerView.ViewHolder { + private ImageView image; + private TextView text; + + public SiteCell(View itemView) { + super(itemView); + image = (ImageView) itemView.findViewById(R.id.image); + text = (TextView) itemView.findViewById(R.id.text); + } + + private void setSite(SetupPresenter.AddedSite site) { + image.setImageDrawable(site.drawable); + text.setText(site.title); + } + } +} diff --git a/Clover/app/src/main/java/org/floens/chan/ui/controller/ThemeSettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/controller/ThemeSettingsController.java index 1d64f380..01404730 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/controller/ThemeSettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/controller/ThemeSettingsController.java @@ -37,6 +37,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import org.floens.chan.R; +import org.floens.chan.chan.ChanParser; import org.floens.chan.controller.Controller; import org.floens.chan.core.model.Board; import org.floens.chan.core.model.Loadable; @@ -249,7 +250,7 @@ public class ThemeSettingsController extends Controller implements View.OnClickL "http://example.com/" + "
" + "Phasellus consequat semper sodales. Donec dolor lectus, aliquet nec mollis vel, rutrum vel enim."); - Post post = getGraph().getChanParser().parse(theme, builder); + Post post = getGraph().get(ChanParser.class).parse(theme, builder); LinearLayout linearLayout = new LinearLayout(themeContext); linearLayout.setOrientation(LinearLayout.VERTICAL); diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java index d946bdf6..6c506ff2 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/FilterLayout.java @@ -239,7 +239,7 @@ public class FilterLayout extends LinearLayout implements View.OnClickListener { final LinearLayout selectLayout = (LinearLayout) LayoutInflater.from(getContext()) .inflate(R.layout.layout_site_board_select, null); - final Spinner spinner = (Spinner) selectLayout.findViewById(R.id.spinner); + final Spinner spinner = (Spinner) selectLayout.findViewById(R.id.progress); final List allSites = Sites.ALL_SITES; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java index ce2936a5..00950eb2 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/ReplyLayout.java @@ -50,7 +50,7 @@ import org.floens.chan.ui.theme.ThemeHelper; import org.floens.chan.ui.view.LoadView; import org.floens.chan.ui.view.SelectionListeningEditText; import org.floens.chan.utils.AndroidUtils; -import org.floens.chan.utils.AnimationUtils; +import org.floens.chan.ui.animation.AnimationUtils; import org.floens.chan.utils.ImageDecoder; import java.io.File; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadListLayout.java b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadListLayout.java index 2be1e7e1..17f5d67e 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadListLayout.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/layout/ThreadListLayout.java @@ -47,7 +47,7 @@ import org.floens.chan.ui.cell.ThreadStatusCell; import org.floens.chan.ui.toolbar.Toolbar; import org.floens.chan.ui.view.ThumbnailView; import org.floens.chan.utils.AndroidUtils; -import org.floens.chan.utils.AnimationUtils; +import org.floens.chan.ui.animation.AnimationUtils; import java.util.Calendar; import java.util.List; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsController.java b/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsController.java index be8ce8d8..51104bf7 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsController.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/settings/SettingsController.java @@ -28,7 +28,7 @@ import android.widget.TextView; import org.floens.chan.R; import org.floens.chan.controller.Controller; import org.floens.chan.utils.AndroidUtils; -import org.floens.chan.utils.AnimationUtils; +import org.floens.chan.ui.animation.AnimationUtils; import java.util.ArrayList; import java.util.List; diff --git a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java index c072defd..25478b43 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/toolbar/Toolbar.java @@ -263,10 +263,19 @@ public class Toolbar extends LinearLayout implements View.OnClickListener { } } + @Override + public boolean onTouchEvent(MotionEvent event) { + return true; + } + public void setArrowMenuProgress(float progress) { arrowMenuDrawable.setProgress(progress); } + public void setShowArrowMenu(boolean show) { + arrowMenuView.setVisibility(show ? VISIBLE : GONE); + } + public ArrowMenuDrawable getArrowMenuDrawable() { return arrowMenuDrawable; } @@ -291,6 +300,8 @@ public class Toolbar extends LinearLayout implements View.OnClickListener { private void init() { setOrientation(HORIZONTAL); + if (isInEditMode()) return; + FrameLayout leftButtonContainer = new FrameLayout(getContext()); addView(leftButtonContainer, LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); @@ -309,7 +320,9 @@ public class Toolbar extends LinearLayout implements View.OnClickListener { addView(navigationItemContainer, new LayoutParams(0, LayoutParams.MATCH_PARENT, 1f)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - setElevation(dp(4f)); + if (getElevation() == 0f) { + setElevation(dp(4f)); + } } } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/LoadView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/LoadView.java index 6e5a410a..ab2a0f9f 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/LoadView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/LoadView.java @@ -28,7 +28,7 @@ import android.view.animation.LinearInterpolator; import android.widget.FrameLayout; import android.widget.ProgressBar; -import org.floens.chan.utils.AnimationUtils; +import org.floens.chan.ui.animation.AnimationUtils; /** * Container for a view with an ProgressBar. Toggles between the view and a @@ -100,6 +100,7 @@ public class LoadView extends FrameLayout { public View setView(View newView, boolean animate) { if (newView == null) { FrameLayout progressBar = new FrameLayout(getContext()); + progressBar.setVisibility(View.GONE); progressBar.addView(new ProgressBar(getContext()), new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.CENTER)); newView = progressBar; } diff --git a/Clover/app/src/main/java/org/floens/chan/ui/view/ThumbnailView.java b/Clover/app/src/main/java/org/floens/chan/ui/view/ThumbnailView.java index d4a608f0..74560b60 100644 --- a/Clover/app/src/main/java/org/floens/chan/ui/view/ThumbnailView.java +++ b/Clover/app/src/main/java/org/floens/chan/ui/view/ThumbnailView.java @@ -105,7 +105,7 @@ public class ThumbnailView extends View implements ImageLoader.ImageListener { } if (!TextUtils.isEmpty(url)) { - container = getGraph().getImageLoader().get(url, this, width, height); + container = getGraph().get(ImageLoader.class).get(url, this, width, height); } } diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_add_black_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_add_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..c04b523c482b77824608a836515e865579f8b00c GIT binary patch literal 124 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8K;v!{z=NCji^0d}{88U+a%3{LE) zc(W3I@VD?TX6u!5iTdQ+%zl7P_JBaSLWcds4UNy(0^e=DSmGkkBjwV6s9vOpiQ(Iw WlC8ByowtC-F?hQAxvX|k0wldT1B8K8l&6bhNCo5D3)Wn%2|O+jnZqxn z_J`%2IDY9((!#y}k7}ws@%iuQbZM1fVun)3_bdIa9|HDHj@lf2n|)>D-rh@()B41; w{>}O`;lIi6&D|#tmaskZlY6$v<FVdQ&MBb@08joqcmMzZ literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_expand_less_black_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_expand_less_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..57139a78aecdf15fe67f908999cfdb55875c0ad3 GIT binary patch literal 149 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8K8jHioZNCo5DGuB)U4g$>&8x|}V z`|f4;ZaMR$Jg&1kNfYf;A2b)gGv68^{;X%K9nVTF=T)a|Ln w%9KMZL>5lzopr0IqE?zyJUM literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-hdpi/ic_expand_more_black_24dp.png b/Clover/app/src/main/res/drawable-hdpi/ic_expand_more_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..9625f148f8ffb91fabf5e93e9a79dc2ff4a7fa66 GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8K8oTrOpNCo5DGp9LQ9eCO<@{1hX zq`d9P>fJkd6I*uv>3*%WyutbB>L9gyXZ|E4FTc3CAekqsa8C2JTp9VbJp7?qNhWb~ zO%|STrA@0z5=;cX|Jl*5m?PYIHXed%m|<=5_cK${pmUHx3vIVCg!0Fk0N A$N&HU literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_add_black_24dp.png b/Clover/app/src/main/res/drawable-mdpi/ic_add_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..23bf119211e02b139ad2e0961bc71662ce1caf1e GIT binary patch literal 86 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1Sx*Bx14mcwEU48mzn9ZOh$WB?6e N@O1TaS?83{1OQ_wCgcDB literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_expand_less_black_24dp.png b/Clover/app/src/main/res/drawable-mdpi/ic_expand_less_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..08c16a328ab1774cf7bda4a61525b05051b6b612 GIT binary patch literal 126 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1S5FtmkP60Ri7N>rCJc(6r~Wf1 zGHagt&!8B?o%nRW5n%T4k;ON9MoW7 YNMw{fmt<}70caqDr>mdKI;Vst01Rd&*8l(j literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-mdpi/ic_expand_more_black_24dp.png b/Clover/app/src/main/res/drawable-mdpi/ic_expand_more_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..feb85a775db5a0a38add95ebbdc4ac6f9129eb12 GIT binary patch literal 125 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*17f%<*kP60Ri75#pFCGaWWa;Ip zmrx3DS=6Y+YjKr%=|5&smqi-?`7b#HaPKNQ6t(EW6ya-40e(xwW)v;j%J5KuZ7~DG YOCH%@2P3>!0nKCZboFyt=akR{03QG+0ssI2 literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_add_black_24dp.png b/Clover/app/src/main/res/drawable-xhdpi/ic_add_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..3191d5283e981b9eecdcaa54feda419515ef656f GIT binary patch literal 108 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}tV^0^ykP61P7Z(aL7zi*O{Br+` z`;vw;dk$=HXVkVR^_;bx1E`8&^BsGK4=F%~hW8~_)0*>!J6N0?fLsPoS3j3^P6+vm9)wmXL{3Jx`WpH%;8rubwo@tA^xt}Eo9ooSxIZoi?< yd^OLU_!HYdiT^a${y9z8Sbok-{&(k@cQAfT;{16naLa3;MGT&nO_`r@5V$XU?42SH^tMqdfas S$Tex8?F^o-elF{r5}E+-dqN)o literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-xhdpi/ic_expand_more_black_24dp.png b/Clover/app/src/main/res/drawable-xhdpi/ic_expand_more_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..d3ee65e9ab4f9a4a000f76b7158bf02df83e9a55 GIT binary patch literal 168 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}b0Dd`}n0kP61PXAg273J_p>nCU(F z7z_Vu)#FTIDy(mk6)Jx2XG-h%-`&xnZmwp{zr_5kp-!2dN$ZD2FB1!&-PY}r6T7W% z&vV4-$=RgG5}(o(KV=ohpZI+=>7KsW=Qp=fS?83{ F1OO~S9V-9; literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_arrow_forward_black_24dp.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_arrow_forward_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..a124259898f2cbaa970223e6b3e995bcf35f4bba GIT binary patch literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhaw8a-VcLn;{G-aO0M;K1YZuw;Sg ziluIAFVFW|^kmcb28&bGmk+R?o|hykC@A>Tc3~Pfx9p`!GLn~OiAV;e&u$gVY+&IO zQt_Ce;P^{ZQ*QR^#;zp-ikuAbP0l+XkKNEt)L literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-xxhdpi/ic_expand_less_black_24dp.png b/Clover/app/src/main/res/drawable-xxhdpi/ic_expand_less_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..ee92f4ecd4663703e2f95bae65ba8592e88ff401 GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhaw=6Jd|hEy=VJ$IIqiBZ7$;mQMC zVcK8SYgcVu(aceO(>Z~;dC&CX2dnpYKtRZ>qm{MGBInFYE3BGz^kt^l^5sAdTY*i` zyyErk&Zml}MacxsyKK3{=D7M(%exm9pITNFUjKBp08Tq0CXvXr>mdKI;Vst0R6^T znKyHTq&TOLipK;6$0imSqouWK>FO^H{i-EZPRrFeK5$IE{(?Wpc>mF7v-AI5{qucB z{=T2j?*A(SlJC!$->>-`zQKR4edwz{D}$cMlUZ89)0gtC|Y`CEg46|8RExAI@7f^-VVS=PR7ccdu_U0y55j zS3mLjFXyK}eib)`TYmmysoZDlI6vP{qxPTMp}Qa1v-iy^khaz={O48ie@}_5wN~jr vuYxo0TTJ8qckncSo~5rghYjW^hNK$CJ-K(&KOMZS2NLsi^>bP0l+XkKy8mlQ literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/drawable-xxxhdpi/ic_expand_more_black_24dp.png b/Clover/app/src/main/res/drawable-xxxhdpi/ic_expand_more_black_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..ad852e3e6f765bb50fc320781ef1c8bd84cf0c9a GIT binary patch literal 256 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD0wg^q?%xcgj(fT|hEy=VJ%7-bC6VXogX;{N zeVybPt8On{UGPLIQ>i=tQ+VUtW{*iKo?!G+IsfK|6V-=)`#s;e;ll}kE72R_R<;o{ zo|ap+KIa#E_VdM|bgR7^3jRKw%stiby!f-9SDcm}=WhP zIsCiJsvsYs@VSxI?*BigY5Y1dl}bNp|$*m|mw>m<9F7W?YAH4ry>PFfQGdg(o}N6X*r1A2+U)78&qol`;+099;o AqW}N^ literal 0 HcmV?d00001 diff --git a/Clover/app/src/main/res/layout/cell_saved_board.xml b/Clover/app/src/main/res/layout/cell_saved_board.xml new file mode 100644 index 00000000..82aa841a --- /dev/null +++ b/Clover/app/src/main/res/layout/cell_saved_board.xml @@ -0,0 +1,42 @@ + + + + + + + + diff --git a/Clover/app/src/main/res/layout/cell_site.xml b/Clover/app/src/main/res/layout/cell_site.xml new file mode 100644 index 00000000..1bf154f4 --- /dev/null +++ b/Clover/app/src/main/res/layout/cell_site.xml @@ -0,0 +1,41 @@ + + + + + + + + diff --git a/Clover/app/src/main/res/layout/controller_board_setup.xml b/Clover/app/src/main/res/layout/controller_board_setup.xml new file mode 100644 index 00000000..774e2783 --- /dev/null +++ b/Clover/app/src/main/res/layout/controller_board_setup.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + diff --git a/Clover/app/src/main/res/layout/controller_site_setup.xml b/Clover/app/src/main/res/layout/controller_site_setup.xml new file mode 100644 index 00000000..880d8a6c --- /dev/null +++ b/Clover/app/src/main/res/layout/controller_site_setup.xml @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + +