├── .gitignore ├── .idea ├── .name ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── 0661c7b2-87e0-11e6-81d6-8c71051e1074.gif ├── Compiled APK ├── .gitkeep └── app-debug.apk ├── Model ├── Novel.java └── Volume.java ├── NovelDetail.java ├── ProfileActivity.java ├── README.md ├── Service ├── CheckForSDCard.java ├── FolioReader.java └── PicassoLoadingService.java ├── app ├── .gitignore ├── build.gradle ├── google-services.json ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── tubes │ │ └── lightnovel │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── tubes │ │ │ └── lightnovel │ │ │ ├── Adapter │ │ │ ├── BannerSliderAdapter.java │ │ │ ├── NovelViewAdapter.java │ │ │ └── VolumeListAdapter.java │ │ │ ├── Common │ │ │ └── Common.java │ │ │ ├── FilterSearchActivity.java │ │ │ ├── Interface │ │ │ ├── IBannerLoadDone.java │ │ │ ├── IClickNovelListener.java │ │ │ └── INovelLoadDone.java │ │ │ ├── Login.java │ │ │ ├── MainActivity.java │ │ │ ├── Model │ │ │ ├── Novel.java │ │ │ └── Volume.java │ │ │ ├── NovelDetail.java │ │ │ ├── ProfileActivity.java │ │ │ ├── Service │ │ │ ├── CheckForSDCard.java │ │ │ ├── DownloadTask.java │ │ │ ├── FolioReader.java │ │ │ └── PicassoLoadingService.java │ │ │ ├── SignUp.java │ │ │ └── SplashScreen.java │ └── res │ │ ├── anim │ │ ├── bottom_animation.xml │ │ └── top_animation.xml │ │ ├── drawable-anydpi │ │ ├── account.xml │ │ ├── add_profile.xml │ │ ├── close_chip.xml │ │ ├── ic_search.xml │ │ └── menu_search.xml │ │ ├── drawable-hdpi │ │ ├── account.png │ │ ├── add_profile.png │ │ ├── close_chip.png │ │ ├── ic_search.png │ │ └── menu_search.png │ │ ├── drawable-mdpi │ │ ├── account.png │ │ ├── add_profile.png │ │ ├── close_chip.png │ │ ├── ic_search.png │ │ ├── logo.png │ │ └── menu_search.png │ │ ├── drawable-v24 │ │ └── font_no.png │ │ ├── drawable-xhdpi │ │ ├── account.png │ │ ├── add_profile.png │ │ ├── close_chip.png │ │ ├── ic_search.png │ │ └── menu_search.png │ │ ├── drawable-xxhdpi │ │ ├── account.png │ │ ├── add_profile.png │ │ ├── close_chip.png │ │ ├── ic_search.png │ │ └── menu_search.png │ │ ├── drawable │ │ └── separator.xml │ │ ├── font │ │ ├── antic.xml │ │ ├── anton.xml │ │ └── bungee.xml │ │ ├── layout │ │ ├── activity_filter_search.xml │ │ ├── activity_login.xml │ │ ├── activity_main.xml │ │ ├── activity_novel_detail.xml │ │ ├── activity_profile.xml │ │ ├── activity_sign_up.xml │ │ ├── activity_splash_screen.xml │ │ ├── chip_item.xml │ │ ├── dialog_options.xml │ │ ├── dialog_search.xml │ │ └── novel_item.xml │ │ ├── menu │ │ ├── menu.xml │ │ └── search_menu.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 │ │ └── values │ │ ├── colors.xml │ │ ├── font_certs.xml │ │ ├── preloaded_fonts.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── tubes │ └── lightnovel │ └── ExampleUnitTest.java ├── asset ├── font.png └── font_no.png ├── build.gradle ├── f42059c4-87df-11e6-97f8-29e61a79e8aa.gif ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── hikari_novel.png ├── layout ├── activity_novel_detail.xml ├── activity_profile.xml ├── dialog_options.xml ├── dialog_search.xml └── novel_item.xml ├── menu ├── menu.xml └── search_menu.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | LightNovel -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /0661c7b2-87e0-11e6-81d6-8c71051e1074.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/0661c7b2-87e0-11e6-81d6-8c71051e1074.gif -------------------------------------------------------------------------------- /Compiled APK/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/Compiled APK/.gitkeep -------------------------------------------------------------------------------- /Compiled APK/app-debug.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/Compiled APK/app-debug.apk -------------------------------------------------------------------------------- /Model/Novel.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Model; 2 | 3 | import java.util.List; 4 | 5 | public class Novel { 6 | private String Name; 7 | private String Image; 8 | private String Category; 9 | private String Sinopsis; 10 | 11 | public String getSinopsis() { 12 | return Sinopsis; 13 | } 14 | 15 | public void setSinopsis(String sinopsis) { 16 | Sinopsis = sinopsis; 17 | } 18 | 19 | private List Volume; 20 | 21 | public String getName() { 22 | return Name; 23 | } 24 | 25 | public void setName(String name) { 26 | Name = name; 27 | } 28 | 29 | public String getImage() { 30 | return Image; 31 | } 32 | 33 | public void setImage(String image) { 34 | Image = image; 35 | } 36 | 37 | public String getCategory() { 38 | return Category; 39 | } 40 | 41 | public void setCategory(String category) { 42 | Category = category; 43 | } 44 | 45 | public List getVolume() { 46 | return Volume; 47 | } 48 | 49 | public void setVolume(List volume) { 50 | Volume = volume; 51 | } 52 | } -------------------------------------------------------------------------------- /Model/Volume.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Model; 2 | 3 | public class Volume { 4 | private String Name; 5 | private String Links; 6 | private String Image; 7 | 8 | public String getName() { 9 | return Name; 10 | } 11 | 12 | public void setName(String name) { 13 | Name = name; 14 | } 15 | 16 | public String getLinks() { 17 | return Links; 18 | } 19 | 20 | public void setLinks(String links) { 21 | Links = links; 22 | } 23 | 24 | public String getImage() { 25 | return Image; 26 | } 27 | 28 | public void setImage(String image) { 29 | Image = image; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /NovelDetail.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel; 2 | 3 | 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | import android.widget.Button; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | import androidx.appcompat.app.AppCompatActivity; 11 | import androidx.recyclerview.widget.GridLayoutManager; 12 | import androidx.recyclerview.widget.RecyclerView; 13 | 14 | import com.squareup.picasso.Picasso; 15 | import com.tubes.lightnovel.Adapter.NovelViewAdapter; 16 | import com.tubes.lightnovel.Adapter.VolumeListAdapter; 17 | import com.tubes.lightnovel.Common.Common; 18 | import com.tubes.lightnovel.Model.Novel; 19 | import com.tubes.lightnovel.Model.Volume; 20 | 21 | import java.util.List; 22 | 23 | public class NovelDetail extends AppCompatActivity { 24 | 25 | TextView sinopsis; 26 | ImageView imageNovel; 27 | 28 | RecyclerView recycleVolume; 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | setContentView(R.layout.activity_novel_detail); 33 | 34 | initialize(); 35 | //set image novel 36 | Log.i("besar volume", ""+Common.novelSelected.getVolume().size()); 37 | Picasso.get().load(Common.novelSelected.getImage()).into(imageNovel); 38 | 39 | //set sinopsis 40 | sinopsis.setText(Common.novelSelected.getSinopsis()); 41 | 42 | //set adapter 43 | onNovelLoadDoneListener(Common.novelSelected.getVolume()); 44 | } 45 | 46 | private void initialize() { 47 | sinopsis = (TextView) findViewById(R.id.sinopsis); 48 | imageNovel = (ImageView) findViewById(R.id.novel_Image); 49 | 50 | recycleVolume = (RecyclerView) findViewById(R.id.recycler_volume); 51 | recycleVolume.setHasFixedSize(true); 52 | recycleVolume.setLayoutManager(new GridLayoutManager(this,2)); 53 | 54 | 55 | } 56 | 57 | public void onNovelLoadDoneListener(List volumeList) { 58 | recycleVolume.setAdapter(new VolumeListAdapter(getBaseContext(), volumeList)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ProfileActivity.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | import androidx.appcompat.app.AppCompatActivity; 6 | 7 | import android.content.Intent; 8 | import android.graphics.Bitmap; 9 | import android.net.Uri; 10 | import android.os.Bundle; 11 | import android.provider.MediaStore; 12 | import android.util.Log; 13 | import android.view.View; 14 | import android.widget.Button; 15 | import android.widget.ImageView; 16 | import android.widget.ProgressBar; 17 | import android.widget.Toast; 18 | 19 | import com.google.android.gms.tasks.OnCompleteListener; 20 | import com.google.android.gms.tasks.OnFailureListener; 21 | import com.google.android.gms.tasks.OnSuccessListener; 22 | import com.google.android.gms.tasks.Task; 23 | import com.google.android.material.textfield.TextInputLayout; 24 | import com.google.firebase.auth.FirebaseAuth; 25 | import com.google.firebase.auth.FirebaseUser; 26 | import com.google.firebase.auth.UserProfileChangeRequest; 27 | import com.google.firebase.storage.FirebaseStorage; 28 | import com.google.firebase.storage.StorageReference; 29 | import com.google.firebase.storage.UploadTask; 30 | import com.squareup.picasso.Picasso; 31 | import com.tubes.lightnovel.Common.Common; 32 | 33 | import java.io.IOException; 34 | 35 | public class ProfileActivity extends AppCompatActivity { 36 | 37 | private static final int CHOOSE_IMAGE = 101; 38 | TextInputLayout displayName; 39 | Button save, logOut; 40 | ImageView imageProfile; 41 | private Uri uriProfileImage; 42 | private ProgressBar progressBar; 43 | private String profileImageUrl; 44 | private FirebaseAuth mAuth; 45 | @Override 46 | protected void onCreate(Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | setContentView(R.layout.activity_profile); 49 | displayName = (TextInputLayout) findViewById(R.id.editTextDisplayName); 50 | save = (Button) findViewById(R.id.save_name); 51 | imageProfile = (ImageView) findViewById(R.id.imageView); 52 | progressBar = (ProgressBar) findViewById(R.id.progressbar); 53 | logOut = (Button) findViewById(R.id.log_out); 54 | //database reference 55 | 56 | mAuth = FirebaseAuth.getInstance(); 57 | 58 | loadProfileDetail(); 59 | 60 | imageProfile.setOnClickListener(new View.OnClickListener() { 61 | @Override 62 | public void onClick(View v) { 63 | showImageChooser(); 64 | } 65 | }); 66 | 67 | save.setOnClickListener(new View.OnClickListener() { 68 | @Override 69 | public void onClick(View v) { 70 | saveUserInformation(); 71 | } 72 | }); 73 | 74 | logOut.setOnClickListener(new View.OnClickListener() { 75 | @Override 76 | public void onClick(View v) { 77 | FirebaseAuth.getInstance().signOut(); 78 | finish(); 79 | startActivity(new Intent(ProfileActivity.this, MainActivity.class)); 80 | } 81 | }); 82 | 83 | 84 | } 85 | 86 | private void loadProfileDetail() { 87 | String display, photoUrl; 88 | photoUrl= ""; 89 | if(mAuth.getCurrentUser()!=null){ 90 | 91 | display = mAuth.getCurrentUser().getDisplayName(); 92 | if(mAuth.getCurrentUser().getPhotoUrl()!=null){ 93 | photoUrl = mAuth.getCurrentUser().getPhotoUrl().toString(); 94 | } 95 | 96 | if(display!=null && !photoUrl.equals("")){ 97 | 98 | displayName.getEditText().setText(display); 99 | Log.i("photoURL", ""+photoUrl); 100 | Picasso.get().load(photoUrl).into(imageProfile); 101 | } 102 | } 103 | 104 | 105 | } 106 | 107 | private void saveUserInformation() { 108 | 109 | 110 | String display = displayName.getEditText().getText().toString(); 111 | 112 | if (display.isEmpty()) { 113 | displayName.setError("Name required"); 114 | displayName.requestFocus(); 115 | return; 116 | } 117 | 118 | 119 | Log.i("update profile : ", display+profileImageUrl); 120 | 121 | if (mAuth.getCurrentUser() != null && profileImageUrl != null) { 122 | UserProfileChangeRequest profile = new UserProfileChangeRequest.Builder() 123 | .setDisplayName(display) 124 | .setPhotoUri(Uri.parse(profileImageUrl)) 125 | .build(); 126 | 127 | mAuth.getCurrentUser().updateProfile(profile) 128 | .addOnCompleteListener(new OnCompleteListener() { 129 | @Override 130 | public void onComplete(@NonNull Task task) { 131 | if (task.isSuccessful()) { 132 | Toast.makeText(ProfileActivity.this, "Profile Updated", Toast.LENGTH_SHORT).show(); 133 | } 134 | } 135 | }); 136 | } 137 | } 138 | 139 | @Override 140 | protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 141 | super.onActivityResult(requestCode, resultCode, data); 142 | 143 | if (requestCode == CHOOSE_IMAGE && resultCode == RESULT_OK && data != null && data.getData() != null) { 144 | uriProfileImage = data.getData(); 145 | try { 146 | Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uriProfileImage); 147 | imageProfile.setImageBitmap(bitmap); 148 | 149 | uploadImageToFirebaseStorage(); 150 | 151 | } catch (IOException e) { 152 | e.printStackTrace(); 153 | } 154 | } 155 | 156 | } 157 | 158 | private void uploadImageToFirebaseStorage() { 159 | final StorageReference profileImageRef = 160 | FirebaseStorage.getInstance().getReference("profilepics/" + mAuth.getCurrentUser().getUid() + ".jpg"); 161 | 162 | if (uriProfileImage != null) { 163 | progressBar.setVisibility(View.VISIBLE); 164 | profileImageRef.putFile(uriProfileImage) 165 | .addOnSuccessListener(new OnSuccessListener() { 166 | @Override 167 | public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { 168 | progressBar.setVisibility(View.GONE); 169 | profileImageUrl = taskSnapshot.getMetadata().getReference().getDownloadUrl().toString(); 170 | 171 | profileImageRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener() { 172 | @Override 173 | public void onSuccess(Uri uri) { 174 | profileImageUrl = uri.toString(); 175 | } 176 | }); 177 | 178 | } 179 | }) 180 | .addOnFailureListener(new OnFailureListener() { 181 | @Override 182 | public void onFailure(@NonNull Exception e) { 183 | progressBar.setVisibility(View.GONE); 184 | Toast.makeText(ProfileActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show(); 185 | } 186 | }); 187 | } 188 | } 189 | 190 | private void showImageChooser() { 191 | Intent intent = new Intent(); 192 | intent.setType("image/*"); 193 | intent.setAction(Intent.ACTION_GET_CONTENT); 194 | startActivityForResult(Intent.createChooser(intent, "Select Profile Image"), CHOOSE_IMAGE); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | HIKARI NOVEL BANNER 3 |

4 | 5 | 6 | ### HIKARI NOVEL 7 | 8 | ### Features 9 | 10 | - [x] Custom Fonts 11 | - [x] Custom Text Size 12 | - [x] Day mode / Night mode 13 | - [x] Handle Link Internal dan External yang ada pada epub Internal and External Links 14 | - [x] Mode Portrait / Landscape 15 | - [x] Kamus Built-in 16 | - [x] Search Novel 17 | - [x] Last Read Locator 18 | 19 | ## Demo 20 | ##### Day and Night Mode 21 | ![Custom fonts](https://raw.githubusercontent.com/ilhamrecca/HikariNovel/main/f42059c4-87df-11e6-97f8-29e61a79e8aa.gif) 22 | ##### Custom Fonts 23 | ![Day night mode](https://raw.githubusercontent.com/ilhamrecca/HikariNovel/main/0661c7b2-87e0-11e6-81d6-8c71051e1074.gif) 24 | 25 | Hikari Novel merupakan aplikasi membaca novel bebasis android. Aplikasi ini menyediakan sejumlah koleksi e-novel bagi reader. Aplikasi ini memiliki beberapa fitur utama yaitu, tracking membaca pengguna , pencarian novel berdasarkan judul atau genre, novel reader dengan tema dark dan light mode, setting font dan ukuran tulisan agar usser mendapat pengalaman membaca yang nyaman. 26 | -------------------------------------------------------------------------------- /Service/CheckForSDCard.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Service; 2 | 3 | import android.os.Environment; 4 | 5 | public class CheckForSDCard { 6 | //Check If SD Card is present or not method 7 | public boolean isSDCardPresent() { 8 | if (Environment.getExternalStorageState().equals( 9 | Environment.MEDIA_MOUNTED)) { 10 | return true; 11 | } 12 | return false; 13 | } 14 | } -------------------------------------------------------------------------------- /Service/FolioReader.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Service; 2 | 3 | import android.util.Log; 4 | import android.widget.Toast; 5 | 6 | import com.folioreader.Config; 7 | import com.folioreader.model.locators.ReadLocator; 8 | import com.folioreader.util.ReadLocatorListener; 9 | import com.google.firebase.auth.FirebaseAuth; 10 | import com.google.firebase.database.DataSnapshot; 11 | import com.google.firebase.database.DatabaseError; 12 | import com.google.firebase.database.DatabaseReference; 13 | import com.google.firebase.database.FirebaseDatabase; 14 | import com.google.firebase.database.ValueEventListener; 15 | import com.tubes.lightnovel.R; 16 | 17 | public class FolioReader { 18 | private com.folioreader.FolioReader fReader; 19 | private Config config; 20 | private FirebaseDatabase database; 21 | private DatabaseReference myRef; 22 | private FirebaseAuth mAuth; 23 | private String path, fileName; 24 | 25 | public FolioReader(String path, String downloadFileName) { 26 | this.path = path; 27 | this.fileName = downloadFileName; 28 | initialize(); 29 | } 30 | 31 | private void initialize() { 32 | //database 33 | database = FirebaseDatabase.getInstance(); 34 | 35 | mAuth = FirebaseAuth.getInstance(); 36 | 37 | 38 | fileName = fileName.substring(0,fileName.length()-5); 39 | Log.i("nama file", fileName); 40 | 41 | //Reader 42 | fReader = com.folioreader.FolioReader.get(); 43 | Config config = new Config(); 44 | config.setNightMode(true); 45 | config.setThemeColorRes(R.color.colorAccent); 46 | config.setAllowedDirection(Config.AllowedDirection.VERTICAL_AND_HORIZONTAL); 47 | config.setDirection(Config.Direction.VERTICAL); 48 | config.setShowTts(false); 49 | fReader.setConfig(config,true); 50 | 51 | if(mAuth.getCurrentUser()!=null) { 52 | myRef = database.getReference("UserLocator/ReadLocator/"+ mAuth.getCurrentUser().getUid()); 53 | checkSavedLocation(); 54 | } 55 | else{ 56 | fReader.openBook(path); 57 | } 58 | 59 | 60 | 61 | 62 | 63 | fReader.setReadLocatorListener(new ReadLocatorListener() { 64 | @Override 65 | public void saveReadLocator(ReadLocator readLocator) { 66 | 67 | Log.i("message", "-> saveReadLocator -> " + readLocator.toJson()); 68 | if(mAuth.getCurrentUser()!=null) { 69 | myRef = database.getReference("UserLocator/ReadLocator/" + mAuth.getCurrentUser().getUid()); 70 | myRef.child(fileName).setValue(readLocator); 71 | } 72 | // Toast.makeText(fReader.getClass().getconte, "Failed to launch Reader", Toast.LENGTH_SHORT).show(); 73 | //fReader.close(); 74 | /*ReadLocator has toJson() method which gives JSON in following format - 75 | { 76 | bookId : string, 77 | href : string, 78 | created : integer, 79 | locations : { 80 | cfi : string 81 | } 82 | } 83 | You can save this last read position in your local or remote db*/ 84 | } 85 | }); 86 | } 87 | 88 | private void checkSavedLocation() { 89 | myRef = database.getReference("UserLocator/ReadLocator/"+ mAuth.getCurrentUser().getUid()+"/"+fileName); 90 | myRef.addListenerForSingleValueEvent(new ValueEventListener() { 91 | @Override 92 | public void onDataChange(DataSnapshot dataSnapshot) { 93 | ReadLocator post = dataSnapshot.getValue(ReadLocator.class); 94 | if(post!=null){ 95 | Log.i("message", "-> hasil database -> " + post.toJson()); 96 | fReader.setReadLocator(post); 97 | } 98 | fReader.openBook(path); 99 | } 100 | 101 | @Override 102 | public void onCancelled(DatabaseError databaseError) { 103 | System.out.println("The read failed: " + databaseError.getCode()); 104 | } 105 | }); 106 | 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Service/PicassoLoadingService.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Service; 2 | 3 | import android.widget.ImageView; 4 | 5 | import com.squareup.picasso.Picasso; 6 | 7 | import ss.com.bannerslider.ImageLoadingService; 8 | 9 | public class PicassoLoadingService implements ImageLoadingService { 10 | @Override 11 | public void loadImage(String url, ImageView imageView) { 12 | Picasso.get().load(url).into(imageView); 13 | } 14 | 15 | @Override 16 | public void loadImage(int resource, ImageView imageView) { 17 | Picasso.get().load(resource).into(imageView); 18 | } 19 | 20 | @Override 21 | public void loadImage(String url, int placeHolder, int errorDrawable, ImageView imageView) { 22 | Picasso.get().load(url).placeholder(placeHolder).error(errorDrawable).into(imageView); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.google.gms.google-services' 3 | 4 | android { 5 | compileSdkVersion 29 6 | buildToolsVersion "29.0.2" 7 | defaultConfig { 8 | applicationId "com.tubes.lightnovel" 9 | minSdkVersion 24 10 | targetSdkVersion 29 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | implementation fileTree(dir: 'libs', include: ['*.jar']) 25 | implementation 'androidx.appcompat:appcompat:1.0.2' 26 | // implementation 'com.android.support:appcompat-v7:28.0.0' 27 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 28 | implementation 'com.google.firebase:firebase-database:16.0.4' 29 | implementation 'com.google.firebase:firebase-auth:16.0.5' 30 | implementation 'com.google.firebase:firebase-storage:16.0.4' 31 | testImplementation 'junit:junit:4.12' 32 | androidTestImplementation 'androidx.test.ext:junit:1.1.0' 33 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 34 | //program dependencies 35 | implementation 'com.github.FolioReader:FolioReader-Android:0.5.4' 36 | implementation 'com.android.support:multidex:1.0.3' // ( for androidx) 37 | implementation 'com.github.nanohttpd.nanohttpd:nanohttpd:2.0.0-Release' 38 | 39 | //image dependencies 40 | //implementation 'com.android.support:design:28.0.0' 41 | implementation 'com.github.chrisbanes:PhotoView:2.1.0' 42 | implementation 'com.github.d-max:spots-dialog:1.1@aar' 43 | implementation 'com.ss.bannerslider:bannerslider:2.0.0' 44 | implementation 'com.wajahatkarim3.easyflipviewpager:easyflipviewpager:1.0.0' 45 | implementation 'com.squareup.picasso:picasso:2.71828' 46 | implementation 'com.google.android.material:material:1.1.0' 47 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" 48 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 49 | testImplementation 'junit:junit:4.12' 50 | androidTestImplementation 'androidx.test.ext:junit:1.1.0' 51 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 52 | implementation 'com.google.android.material:material:1.1.0' 53 | configurations.matching { it.name == '_internal_aapt2_binary' }.all { 54 | config -> 55 | config.resolutionStrategy.eachDependency { 56 | details -> details.useVersion("3.3.2-5309881") 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "972768762065", 4 | "firebase_url": "https://hikari-novel.firebaseio.com", 5 | "project_id": "hikari-novel", 6 | "storage_bucket": "hikari-novel.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:972768762065:android:76227a492567489eac0df0", 12 | "android_client_info": { 13 | "package_name": "com.tubes.lightnovel" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "972768762065-8ahs8o9kpaaqjitom64k8emj6u6fntp0.apps.googleusercontent.com", 19 | "client_type": 1, 20 | "android_info": { 21 | "package_name": "com.tubes.lightnovel", 22 | "certificate_hash": "309e40a866d3c10c74c4bd25c438d40d5950fbe9" 23 | } 24 | }, 25 | { 26 | "client_id": "972768762065-bvvnlbvvu92d40ait3bho63lq6mgo0j3.apps.googleusercontent.com", 27 | "client_type": 3 28 | } 29 | ], 30 | "api_key": [ 31 | { 32 | "current_key": "AIzaSyAUd4UzPHtGvufm3uwPBYZlAfI0lXTznhE" 33 | } 34 | ], 35 | "services": { 36 | "appinvite_service": { 37 | "other_platform_oauth_client": [ 38 | { 39 | "client_id": "972768762065-bvvnlbvvu92d40ait3bho63lq6mgo0j3.apps.googleusercontent.com", 40 | "client_type": 3 41 | } 42 | ] 43 | } 44 | } 45 | } 46 | ], 47 | "configuration_version": "1" 48 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/tubes/lightnovel/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | 25 | assertEquals("com.tubes.lightnovel", appContext.getPackageName()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/Adapter/BannerSliderAdapter.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Adapter; 2 | 3 | import java.util.List; 4 | 5 | import ss.com.bannerslider.adapters.SliderAdapter; 6 | import ss.com.bannerslider.viewholder.ImageSlideViewHolder; 7 | 8 | public class BannerSliderAdapter extends SliderAdapter { 9 | public List imageList; 10 | 11 | public BannerSliderAdapter(List imageList) { 12 | this.imageList = imageList; 13 | } 14 | 15 | @Override 16 | public int getItemCount() { 17 | return imageList.size(); 18 | } 19 | 20 | @Override 21 | public void onBindImageSlide(int position, ImageSlideViewHolder imageSlideViewHolder) { 22 | imageSlideViewHolder.bindImageSlide(imageList.get(position)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/Adapter/NovelViewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Adapter; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.telephony.IccOpenLogicalChannelResponse; 6 | import android.util.Log; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.ImageView; 11 | import android.widget.TextView; 12 | 13 | import androidx.annotation.NonNull; 14 | import androidx.recyclerview.widget.RecyclerView; 15 | 16 | import com.squareup.picasso.Picasso; 17 | import com.tubes.lightnovel.Common.Common; 18 | import com.tubes.lightnovel.Interface.IClickNovelListener; 19 | import com.tubes.lightnovel.Model.Novel; 20 | import com.tubes.lightnovel.NovelDetail; 21 | import com.tubes.lightnovel.R; 22 | 23 | import java.util.List; 24 | 25 | public class NovelViewAdapter extends RecyclerView.Adapter { 26 | 27 | Context context; 28 | List novelList; 29 | LayoutInflater inflater; 30 | 31 | 32 | 33 | public NovelViewAdapter(Context context, List novelList) { 34 | this.context = context; 35 | this.novelList = novelList; 36 | this.inflater = LayoutInflater.from(context); 37 | } 38 | 39 | @NonNull 40 | @Override 41 | public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 42 | View itemView = inflater.inflate(R.layout.novel_item, parent, false); 43 | return new MyViewHolder((itemView)); 44 | } 45 | 46 | @Override 47 | public void onBindViewHolder(@NonNull MyViewHolder holder, int position) { 48 | Picasso.get().load(novelList.get(position).getImage()).into(holder.imageNovel); 49 | holder.nameNovel.setText(novelList.get(position).getName()); 50 | 51 | 52 | 53 | //event 54 | holder.setClickNovelListener(new IClickNovelListener() { 55 | @Override 56 | public void onClick(View view, int position) { 57 | Common.novelSelected = novelList.get(position); 58 | Intent i = new Intent(context,NovelDetail.class); 59 | i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 60 | context.startActivity(i); 61 | } 62 | }); 63 | } 64 | 65 | @Override 66 | public int getItemCount() { 67 | return novelList.size(); 68 | } 69 | 70 | public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 71 | 72 | TextView nameNovel; 73 | ImageView imageNovel; 74 | 75 | IClickNovelListener clickNovelListener; 76 | 77 | public void setClickNovelListener(IClickNovelListener clickNovelListener) { 78 | this.clickNovelListener = clickNovelListener; 79 | } 80 | 81 | public MyViewHolder(@NonNull View itemView) { 82 | super(itemView); 83 | 84 | nameNovel = (TextView) itemView.findViewById(R.id.name_novel); 85 | imageNovel = (ImageView) itemView.findViewById(R.id.image_novel); 86 | itemView.setOnClickListener(this); 87 | } 88 | 89 | @Override 90 | public void onClick(View v) { 91 | clickNovelListener.onClick(v, getAdapterPosition()); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/Adapter/VolumeListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Adapter; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | import android.util.Log; 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 androidx.annotation.NonNull; 13 | import androidx.recyclerview.widget.RecyclerView; 14 | 15 | import com.squareup.picasso.Picasso; 16 | import com.tubes.lightnovel.Interface.IClickNovelListener; 17 | import com.tubes.lightnovel.Model.Volume; 18 | import com.tubes.lightnovel.R; 19 | import com.tubes.lightnovel.Service.DownloadTask; 20 | import com.tubes.lightnovel.Service.FolioReader; 21 | 22 | import java.io.File; 23 | import java.util.List; 24 | 25 | public class VolumeListAdapter extends RecyclerView.Adapter { 26 | 27 | Context context; 28 | List volumeList; 29 | LayoutInflater inflater; 30 | 31 | 32 | public VolumeListAdapter(Context context, List volumeList) { 33 | this.context = context; 34 | this.volumeList = volumeList; 35 | this.inflater = LayoutInflater.from(context); 36 | } 37 | 38 | @NonNull 39 | @Override 40 | public VolumeListAdapter.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 41 | View itemView = inflater.inflate(R.layout.novel_item, parent, false); 42 | return new MyViewHolder((itemView)); 43 | } 44 | 45 | @Override 46 | public void onBindViewHolder(@NonNull final VolumeListAdapter.MyViewHolder holder, int position) { 47 | Picasso.get().load(volumeList.get(position).getImage()).into(holder.imageNovel); 48 | holder.nameNovel.setText(volumeList.get(position).getName()); 49 | Log.i("kampret",""+volumeList.get(position).getImage()); 50 | Log.i("entahlah",""+volumeList.get(position).getName()); 51 | final Context kontek = this.context; 52 | //event 53 | holder.setClickNovelListener(new IClickNovelListener() { 54 | @Override 55 | public void onClick(View view, int position) { 56 | String downloadLink = volumeList.get(position).getLinks(); 57 | downloadLink = downloadLink.trim(); 58 | Log.i("Download URL",""+downloadLink); 59 | 60 | String path = Environment.getExternalStorageDirectory() + "/LightNovel/" + volumeList.get(position).getName(); 61 | 62 | File file = new File(path); 63 | if(file.exists()){ 64 | new FolioReader(path, volumeList.get(position).getName()); 65 | } 66 | else 67 | new DownloadTask(holder.imageNovel.getContext(), downloadLink, volumeList.get(position).getName()); 68 | } 69 | }); 70 | } 71 | 72 | @Override 73 | public int getItemCount() { 74 | return volumeList.size(); 75 | } 76 | 77 | public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 78 | 79 | TextView nameNovel; 80 | ImageView imageNovel; 81 | 82 | IClickNovelListener clickNovelListener; 83 | 84 | public void setClickNovelListener(IClickNovelListener clickNovelListener) { 85 | this.clickNovelListener = clickNovelListener; 86 | } 87 | 88 | public MyViewHolder(@NonNull View itemView) { 89 | super(itemView); 90 | 91 | nameNovel = (TextView) itemView.findViewById(R.id.name_novel); 92 | imageNovel = (ImageView) itemView.findViewById(R.id.image_novel); 93 | itemView.setOnClickListener(this); 94 | } 95 | 96 | @Override 97 | public void onClick(View v) { 98 | clickNovelListener.onClick(v, getAdapterPosition()); 99 | } 100 | } 101 | } 102 | 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/Common/Common.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Common; 2 | 3 | import com.tubes.lightnovel.Model.Novel; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class Common { 9 | public static List novelList = new ArrayList<>(); 10 | 11 | public static Novel novelSelected; 12 | public static String[] categories = { 13 | "Action", 14 | "Adult", 15 | "Adventure", 16 | "Comedy", 17 | "Completed", 18 | "Cooking", 19 | "Doujinshi", 20 | "Drama", 21 | "Drop", 22 | "Ecchi", 23 | "Fantasy", 24 | "Gender bender", 25 | "Harem", 26 | "Historical", 27 | "Horror", 28 | "Jose", 29 | "Latest", 30 | "Manhua", 31 | "Manhwa", 32 | "Material arts", 33 | "Mature", 34 | "Mecha", 35 | "Medical", 36 | "Mystery", 37 | "Newest", 38 | "One shot", 39 | "Ongoing", 40 | "Psychological", 41 | "Romance", 42 | "School life", 43 | "Sci fi", 44 | "Seinen", 45 | "Shoujo", 46 | "Shoujo a", 47 | "Shounen", 48 | "Shounen ai", 49 | "Slice of life", 50 | "Smut", 51 | "Sports", 52 | "Superhero", 53 | "Supernatural", 54 | "Top Read", 55 | "Tragedy", 56 | "Webtoons", 57 | "Yaoi", 58 | "Yuri" 59 | 60 | 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/FilterSearchActivity.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.appcompat.app.ActionBar; 5 | import androidx.appcompat.app.AlertDialog; 6 | import androidx.appcompat.app.AppCompatActivity; 7 | import androidx.recyclerview.widget.GridLayoutManager; 8 | import androidx.recyclerview.widget.RecyclerView; 9 | 10 | import android.content.DialogInterface; 11 | import android.os.Bundle; 12 | import android.view.LayoutInflater; 13 | import android.view.MenuItem; 14 | import android.view.View; 15 | import android.widget.AdapterView; 16 | import android.widget.ArrayAdapter; 17 | import android.widget.AutoCompleteTextView; 18 | import android.widget.EditText; 19 | import android.widget.TextView; 20 | import android.widget.Toast; 21 | 22 | import com.google.android.material.bottomnavigation.BottomNavigationView; 23 | import com.google.android.material.chip.Chip; 24 | import com.google.android.material.chip.ChipGroup; 25 | import com.tubes.lightnovel.Adapter.NovelViewAdapter; 26 | import com.tubes.lightnovel.Common.Common; 27 | import com.tubes.lightnovel.Model.Novel; 28 | 29 | import java.lang.reflect.Array; 30 | import java.util.ArrayList; 31 | import java.util.Collections; 32 | import java.util.List; 33 | 34 | public class FilterSearchActivity extends AppCompatActivity { 35 | 36 | BottomNavigationView bottomNavigationView; 37 | RecyclerView recyclerView; 38 | 39 | @Override 40 | protected void onCreate(Bundle savedInstanceState) { 41 | super.onCreate(savedInstanceState); 42 | setContentView(R.layout.activity_filter_search); 43 | 44 | ActionBar actionBar = getSupportActionBar(); 45 | actionBar.hide(); 46 | //recycler view 47 | recyclerView = (RecyclerView) findViewById(R.id.recycler_search); 48 | recyclerView.setHasFixedSize(true); 49 | recyclerView.setLayoutManager(new GridLayoutManager(this, 2)); 50 | 51 | //bottom nav 52 | bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_nav); 53 | bottomNavigationView.inflateMenu(R.menu.search_menu); 54 | 55 | bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() { 56 | @Override 57 | public boolean onNavigationItemSelected(@NonNull MenuItem item) { 58 | switch (item.getItemId()){ 59 | 60 | case R.id.filter_action: 61 | showFilterDialog(); 62 | break; 63 | case R.id.search_action: 64 | showSearchDialog(); 65 | break; 66 | default: 67 | break; 68 | 69 | } 70 | 71 | return true; 72 | } 73 | }); 74 | 75 | 76 | } 77 | 78 | private void showSearchDialog() { 79 | AlertDialog.Builder alertDialog= new AlertDialog.Builder(FilterSearchActivity.this); 80 | alertDialog.setTitle("Search"); 81 | 82 | final LayoutInflater inflater = this.getLayoutInflater(); 83 | View searchLayout = inflater.inflate(R.layout.dialog_search,null); 84 | 85 | final EditText querySearch = (EditText) searchLayout.findViewById(R.id.search_query); 86 | alertDialog.setView(searchLayout); 87 | alertDialog.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() { 88 | @Override 89 | public void onClick(DialogInterface dialog, int which) { 90 | dialog.dismiss(); 91 | } 92 | }); 93 | alertDialog.setPositiveButton("SEARCH", new DialogInterface.OnClickListener() { 94 | @Override 95 | public void onClick(DialogInterface dialog, int which) { 96 | fetchSearchNovel(querySearch.getText().toString()); 97 | } 98 | }); 99 | 100 | alertDialog.show(); 101 | } 102 | 103 | private void fetchSearchNovel(String filterQuery) { 104 | List filtered = new ArrayList<>(); 105 | for(Novel novel:Common.novelList){ 106 | if(novel.getName().toLowerCase().contains(filterQuery.toLowerCase())){ 107 | filtered.add(novel); 108 | } 109 | } 110 | 111 | recyclerView.setAdapter(new NovelViewAdapter(getBaseContext(),filtered)); 112 | 113 | } 114 | 115 | private void showFilterDialog() { 116 | AlertDialog.Builder alertDialog= new AlertDialog.Builder(FilterSearchActivity.this); 117 | alertDialog.setTitle("Select Category"); 118 | 119 | final LayoutInflater inflater = this.getLayoutInflater(); 120 | View filter_Layout = inflater.inflate(R.layout.dialog_options,null); 121 | 122 | final AutoCompleteTextView fill_category = (AutoCompleteTextView) filter_Layout.findViewById(R.id.fill_category); 123 | final ChipGroup chipGroup = (ChipGroup) filter_Layout.findViewById(R.id.chipgroup); 124 | 125 | //for autocomplete 126 | ArrayAdapter adapter = new ArrayAdapter<>(this, android.R.layout.select_dialog_item, Common.categories); 127 | fill_category.setAdapter(adapter); 128 | fill_category.setOnItemClickListener(new AdapterView.OnItemClickListener() { 129 | @Override 130 | public void onItemClick(AdapterView parent, View view, int position, long id) { 131 | //clear 132 | fill_category.setText(""); 133 | 134 | //with tags 135 | Chip chip = (Chip) inflater.inflate(R.layout.chip_item,null,false); 136 | chip.setText(((TextView)view).getText()); 137 | chip.setOnCloseIconClickListener(new View.OnClickListener() { 138 | @Override 139 | public void onClick(View v) { 140 | chipGroup.removeView(v); 141 | } 142 | }); 143 | chipGroup.addView(chip); 144 | } 145 | }); 146 | 147 | alertDialog.setView(filter_Layout); 148 | alertDialog.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() { 149 | @Override 150 | public void onClick(DialogInterface dialog, int which) { 151 | dialog.dismiss(); 152 | } 153 | }); 154 | alertDialog.setPositiveButton("FILTER", new DialogInterface.OnClickListener() { 155 | @Override 156 | public void onClick(DialogInterface dialog, int which) { 157 | List filterKey = new ArrayList<>(); 158 | StringBuilder filterQuery = new StringBuilder(""); 159 | 160 | for(int i = 0; i filtered = new ArrayList<>(); 182 | for(Novel novel:Common.novelList){ 183 | if(novel.getCategory()!=null) { 184 | if (novel.getCategory().contains(filterQuery)) { 185 | filtered.add(novel); 186 | } 187 | } 188 | } 189 | if(filtered.size()!=0) 190 | recyclerView.setAdapter(new NovelViewAdapter(getBaseContext(),filtered)); 191 | else 192 | Toast.makeText(this, "No Result", Toast.LENGTH_LONG).show(); 193 | 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/Interface/IBannerLoadDone.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Interface; 2 | 3 | import java.util.List; 4 | 5 | public interface IBannerLoadDone { 6 | void onBannerLoadDoneListener(List banners); 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/Interface/IClickNovelListener.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Interface; 2 | 3 | import android.view.View; 4 | 5 | public interface IClickNovelListener { 6 | void onClick (View view, int position); 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/Interface/INovelLoadDone.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Interface; 2 | 3 | import com.tubes.lightnovel.Model.Novel; 4 | 5 | import java.util.List; 6 | 7 | public interface INovelLoadDone { 8 | void onNovelLoadDoneListener(List novel); 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/Login.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.appcompat.app.ActionBar; 5 | import androidx.appcompat.app.AppCompatActivity; 6 | 7 | import android.app.ActivityOptions; 8 | import android.content.Intent; 9 | import android.os.Bundle; 10 | import android.util.Log; 11 | import android.util.Pair; 12 | import android.util.Patterns; 13 | import android.view.View; 14 | import android.view.WindowManager; 15 | import android.widget.Button; 16 | import android.widget.ImageView; 17 | import android.widget.TextView; 18 | import android.widget.Toast; 19 | 20 | import com.google.android.gms.tasks.OnCompleteListener; 21 | import com.google.android.gms.tasks.Task; 22 | import com.google.android.material.textfield.TextInputLayout; 23 | import com.google.firebase.auth.AuthResult; 24 | import com.google.firebase.auth.FirebaseAuth; 25 | import com.google.firebase.auth.FirebaseAuthUserCollisionException; 26 | 27 | public class Login extends AppCompatActivity { 28 | 29 | private Button callSignUp,login_btn,continueNow; 30 | private ImageView image; 31 | private TextView logoText, sloganText; 32 | private TextInputLayout username,password; 33 | private FirebaseAuth mAuth; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN); 39 | setContentView(R.layout.activity_login); 40 | 41 | //hooks 42 | callSignUp = findViewById(R.id.go_signup_screen); 43 | image = findViewById(R.id.Logo_image); 44 | logoText = findViewById(R.id.Logo_name); 45 | sloganText = findViewById(R.id.slogan_name); 46 | username = findViewById(R.id.username); 47 | password = findViewById(R.id.password); 48 | login_btn = findViewById(R.id.Login_btn); 49 | continueNow = findViewById(R.id.continue_now); 50 | //database reference 51 | mAuth = FirebaseAuth.getInstance(); 52 | 53 | 54 | ActionBar actionBar = getSupportActionBar(); 55 | actionBar.hide(); 56 | 57 | continueNow.setOnClickListener(new View.OnClickListener() { 58 | @Override 59 | public void onClick(View v) { 60 | finish(); 61 | startActivity(new Intent(Login.this, MainActivity.class)); 62 | } 63 | }); 64 | 65 | callSignUp.setOnClickListener(new View.OnClickListener() { 66 | @Override 67 | public void onClick(View view) { 68 | Intent intent = new Intent(Login.this,SignUp.class); 69 | Pair[] pairs = new Pair[7]; 70 | 71 | pairs[0] = new Pair(image,"logo_image"); 72 | pairs[1] = new Pair(logoText,"logo_text"); 73 | pairs[2] = new Pair(sloganText,"logo_desc"); 74 | pairs[3] = new Pair(username,"username_tran"); 75 | pairs[4] = new Pair(password,"password_tran"); 76 | pairs[5] = new Pair(login_btn,"button_tran"); 77 | pairs[6] = new Pair(callSignUp,"login_signup_tran"); 78 | 79 | ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(Login.this,pairs); 80 | startActivity(intent, options.toBundle()); 81 | 82 | } 83 | }); 84 | 85 | login_btn.setOnClickListener(new View.OnClickListener(){ 86 | 87 | @Override 88 | public void onClick(View v) { 89 | userLogin(); 90 | } 91 | }); 92 | 93 | } 94 | 95 | private void userLogin() { 96 | String email = username.getEditText().getText().toString().trim(); 97 | String pass = password.getEditText().getText().toString().trim(); 98 | 99 | if(email.isEmpty()){ 100 | username.setError("Email is required"); 101 | username.requestFocus(); 102 | return; 103 | } 104 | Log.i("Email", "fsdafsd"+email); 105 | if(!Patterns.EMAIL_ADDRESS.matcher(email).matches()){ 106 | username.setError("Please enter a valid email address"); 107 | username.requestFocus(); 108 | return; 109 | } 110 | 111 | if(pass.isEmpty()){ 112 | password.setError("Password is required"); 113 | password.requestFocus(); 114 | return; 115 | } 116 | 117 | if(pass.length()<6){ 118 | password.setError("Minimum length of password is 6"); 119 | password.requestFocus(); 120 | return; 121 | } 122 | 123 | mAuth.signInWithEmailAndPassword(email, pass).addOnCompleteListener(new OnCompleteListener() { 124 | @Override 125 | public void onComplete(@NonNull Task task) { 126 | if(task.isSuccessful()){ 127 | Intent intent = new Intent(Login.this, MainActivity.class); 128 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 129 | startActivity(intent); 130 | finish(); 131 | Toast.makeText(getApplicationContext(),"Welcome Fellow Daoist", Toast.LENGTH_LONG).show(); 132 | } 133 | else { 134 | Toast.makeText(getApplicationContext(), task.getException().getMessage(), Toast.LENGTH_LONG).show(); 135 | } 136 | } 137 | }); 138 | 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | import androidx.core.app.ActivityCompat; 6 | import androidx.recyclerview.widget.GridLayoutManager; 7 | import androidx.recyclerview.widget.RecyclerView; 8 | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; 9 | 10 | import android.Manifest; 11 | import android.content.Intent; 12 | import android.content.pm.PackageManager; 13 | import android.os.Build; 14 | import android.os.Bundle; 15 | import android.util.Log; 16 | import android.view.Menu; 17 | import android.view.MenuInflater; 18 | import android.view.MenuItem; 19 | import android.view.View; 20 | import android.widget.Button; 21 | import android.widget.Filter; 22 | import android.widget.TextView; 23 | import android.widget.Toast; 24 | 25 | import com.folioreader.Config; 26 | import com.folioreader.FolioReader; 27 | import com.folioreader.model.locators.ReadLocator; 28 | 29 | import com.folioreader.util.ReadLocatorListener; 30 | import com.google.firebase.auth.FirebaseAuth; 31 | import com.google.firebase.database.DataSnapshot; 32 | import com.google.firebase.database.DatabaseError; 33 | import com.google.firebase.database.DatabaseReference; 34 | import com.google.firebase.database.FirebaseDatabase; 35 | import com.google.firebase.database.ValueEventListener; 36 | import com.tubes.lightnovel.Adapter.BannerSliderAdapter; 37 | import com.tubes.lightnovel.Adapter.NovelViewAdapter; 38 | import com.tubes.lightnovel.Common.Common; 39 | import com.tubes.lightnovel.Interface.IBannerLoadDone; 40 | import com.tubes.lightnovel.Interface.INovelLoadDone; 41 | import com.tubes.lightnovel.Model.Novel; 42 | import com.tubes.lightnovel.Service.PicassoLoadingService; 43 | 44 | import java.util.ArrayList; 45 | import java.util.List; 46 | 47 | import dmax.dialog.SpotsDialog; 48 | import ss.com.bannerslider.Slider; 49 | 50 | public class MainActivity extends AppCompatActivity implements IBannerLoadDone, INovelLoadDone { 51 | 52 | //database variable 53 | DatabaseReference banners; 54 | DatabaseReference novel; 55 | 56 | //layout slider, button, dll 57 | RecyclerView recyclerNovel; 58 | TextView txtNovel; 59 | Slider slider; 60 | SwipeRefreshLayout swipeRefreshLayout; 61 | 62 | //Listener 63 | IBannerLoadDone bannerLoadDoneListener; 64 | INovelLoadDone novelLoadDone; 65 | 66 | //actionbar button 67 | Button search; 68 | 69 | //alert dialog 70 | android.app.AlertDialog alertDialog; 71 | private FirebaseAuth mAuth; 72 | 73 | @Override 74 | protected void onCreate(Bundle savedInstanceState) { 75 | super.onCreate(savedInstanceState); 76 | setContentView(R.layout.activity_main); 77 | if(!isStoragePermissionGranted()){ 78 | ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); 79 | } 80 | 81 | initialize(); 82 | 83 | //setFolio(); 84 | 85 | 86 | 87 | } 88 | 89 | private void initialize() { 90 | //init dataabase 91 | banners = FirebaseDatabase.getInstance().getReference("Banners"); 92 | novel = FirebaseDatabase.getInstance().getReference("Novel"); 93 | mAuth = FirebaseAuth.getInstance(); 94 | //init layout, button dll. 95 | slider = (Slider) findViewById(R.id.slider); 96 | Slider.init(new PicassoLoadingService()); 97 | recyclerNovel = (RecyclerView) findViewById(R.id.recycler_novel); 98 | recyclerNovel.setHasFixedSize(true); 99 | recyclerNovel.setLayoutManager(new GridLayoutManager(this,2)); 100 | 101 | //init listener banner 102 | bannerLoadDoneListener = this; 103 | novelLoadDone = this; 104 | 105 | swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh); 106 | 107 | swipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary,R.color.colorAccent); 108 | swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { 109 | @Override 110 | public void onRefresh() { 111 | loadBanner(); 112 | loadNovel(); 113 | swipeRefreshLayout.setRefreshing(false); 114 | } 115 | }); 116 | swipeRefreshLayout.post(new Runnable() { 117 | @Override 118 | public void run() { 119 | loadBanner(); 120 | loadNovel(); 121 | } 122 | }); 123 | 124 | 125 | 126 | 127 | } 128 | 129 | private void loadBanner() { 130 | banners.addListenerForSingleValueEvent(new ValueEventListener() { 131 | @Override 132 | public void onDataChange(@NonNull DataSnapshot dataSnapshot) { 133 | List bannerList = new ArrayList<>(); 134 | 135 | for(DataSnapshot bannerSnapshot:dataSnapshot.getChildren()){ 136 | String image = bannerSnapshot.getValue(String.class); 137 | bannerList.add(image); 138 | } 139 | onBannerLoadDoneListener(bannerList); 140 | 141 | } 142 | 143 | @Override 144 | public void onCancelled(@NonNull DatabaseError databaseError) { 145 | Toast.makeText(MainActivity.this,""+databaseError.getMessage(), Toast.LENGTH_LONG).show(); 146 | } 147 | }); 148 | } 149 | @Override 150 | public void onBannerLoadDoneListener(List banners) { 151 | slider.setAdapter(new BannerSliderAdapter(banners)); 152 | } 153 | 154 | private void loadNovel() { 155 | //show dialog 156 | alertDialog = new SpotsDialog.Builder().setContext(this) 157 | .setCancelable(false) 158 | .setMessage("Please Wait") 159 | .build(); 160 | 161 | if(!swipeRefreshLayout.isRefreshing()) 162 | alertDialog.show(); 163 | 164 | novel.addListenerForSingleValueEvent(new ValueEventListener() { 165 | @Override 166 | public void onDataChange(@NonNull DataSnapshot dataSnapshot) { 167 | List novelList = new ArrayList<>(); 168 | 169 | for(DataSnapshot bannerSnapshot:dataSnapshot.getChildren()){ 170 | Novel novel = bannerSnapshot.getValue(Novel.class); 171 | novelList.add(novel); 172 | } 173 | 174 | novelLoadDone.onNovelLoadDoneListener(novelList); 175 | 176 | } 177 | 178 | @Override 179 | public void onCancelled(@NonNull DatabaseError databaseError) { 180 | Toast.makeText(MainActivity.this,""+databaseError.getMessage(), Toast.LENGTH_LONG).show(); 181 | } 182 | }); 183 | } 184 | 185 | public void onNovelLoadDoneListener(List novelList) { 186 | Common.novelList = novelList; 187 | recyclerNovel.setAdapter(new NovelViewAdapter(getBaseContext(), novelList)); 188 | 189 | 190 | if(!swipeRefreshLayout.isRefreshing()){ 191 | alertDialog.dismiss(); 192 | } 193 | } 194 | 195 | @Override 196 | public boolean onCreateOptionsMenu(Menu menu) { 197 | // R.menu.mymenu is a reference to an xml file named mymenu.xml which should be inside your res/menu directory. 198 | // If you don't have res/menu, just create a directory named "menu" inside res 199 | getMenuInflater().inflate(R.menu.menu, menu); 200 | return super.onCreateOptionsMenu(menu); 201 | } 202 | @Override 203 | public boolean onOptionsItemSelected(MenuItem item) { 204 | int id = item.getItemId(); 205 | 206 | if (id == R.id.search) { 207 | Intent intent = new Intent(MainActivity.this, FilterSearchActivity.class); 208 | startActivity(intent); 209 | } 210 | else if(id == R.id.account){ 211 | if(mAuth.getCurrentUser()!=null){ 212 | Intent intent = new Intent(MainActivity.this, ProfileActivity.class); 213 | 214 | startActivity(intent); 215 | } 216 | else{ 217 | Intent intent = new Intent(MainActivity.this, Login.class); 218 | startActivity(intent); 219 | } 220 | 221 | } 222 | return super.onOptionsItemSelected(item); 223 | } 224 | private void setFolio() { 225 | FolioReader folioReader = FolioReader.get(); 226 | Config config = new Config(); 227 | config.setNightMode(true); 228 | config.setThemeColorRes(R.color.colorPrimary); 229 | config.setAllowedDirection(Config.AllowedDirection.VERTICAL_AND_HORIZONTAL); 230 | config.setDirection(Config.Direction.VERTICAL); 231 | config.setShowTts(false); 232 | folioReader.setConfig(config,true); 233 | 234 | folioReader.openBook("/storage/emulated/0/Overlord Volume 01 - The Undead King.epub"); 235 | 236 | folioReader.setReadLocatorListener(new ReadLocatorListener() { 237 | @Override 238 | public void saveReadLocator(ReadLocator readLocator) { 239 | 240 | Log.i("message", "-> saveReadLocator -> " + readLocator.toJson()); 241 | 242 | /*ReadLocator has toJson() method which gives JSON in following format - 243 | { 244 | bookId : string, 245 | href : string, 246 | created : integer, 247 | locations : { 248 | cfi : string 249 | } 250 | } 251 | You can save this last read position in your local or remote db*/ 252 | } 253 | }); 254 | } 255 | 256 | private boolean isStoragePermissionGranted() { 257 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 258 | if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) 259 | == PackageManager.PERMISSION_GRANTED) { 260 | Log.v("permission","Permission is granted"); 261 | return true; 262 | } else { 263 | 264 | Log.v("entahlah","Permission is revoked"); 265 | ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); 266 | return false; 267 | } 268 | } 269 | else { //permission is automatically granted on sdk<23 upon installation 270 | Log.v("permission","Permission is granted"); 271 | return true; 272 | } 273 | } 274 | 275 | 276 | 277 | } -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/Model/Novel.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Model; 2 | 3 | import java.util.List; 4 | 5 | public class Novel { 6 | private String Name; 7 | private String Image; 8 | private String Category; 9 | private String Sinopsis; 10 | 11 | public String getSinopsis() { 12 | return Sinopsis; 13 | } 14 | 15 | public void setSinopsis(String sinopsis) { 16 | Sinopsis = sinopsis; 17 | } 18 | 19 | private List Volume; 20 | 21 | public String getName() { 22 | return Name; 23 | } 24 | 25 | public void setName(String name) { 26 | Name = name; 27 | } 28 | 29 | public String getImage() { 30 | return Image; 31 | } 32 | 33 | public void setImage(String image) { 34 | Image = image; 35 | } 36 | 37 | public String getCategory() { 38 | return Category; 39 | } 40 | 41 | public void setCategory(String category) { 42 | Category = category; 43 | } 44 | 45 | public List getVolume() { 46 | return Volume; 47 | } 48 | 49 | public void setVolume(List volume) { 50 | Volume = volume; 51 | } 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/Model/Volume.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Model; 2 | 3 | public class Volume { 4 | private String Name; 5 | private String Links; 6 | private String Image; 7 | 8 | public String getName() { 9 | return Name; 10 | } 11 | 12 | public void setName(String name) { 13 | Name = name; 14 | } 15 | 16 | public String getLinks() { 17 | return Links; 18 | } 19 | 20 | public void setLinks(String links) { 21 | Links = links; 22 | } 23 | 24 | public String getImage() { 25 | return Image; 26 | } 27 | 28 | public void setImage(String image) { 29 | Image = image; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/NovelDetail.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel; 2 | 3 | 4 | import android.os.Bundle; 5 | import android.util.Log; 6 | import android.widget.Button; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | import androidx.appcompat.app.AppCompatActivity; 11 | import androidx.recyclerview.widget.GridLayoutManager; 12 | import androidx.recyclerview.widget.RecyclerView; 13 | 14 | import com.squareup.picasso.Picasso; 15 | import com.tubes.lightnovel.Adapter.NovelViewAdapter; 16 | import com.tubes.lightnovel.Adapter.VolumeListAdapter; 17 | import com.tubes.lightnovel.Common.Common; 18 | import com.tubes.lightnovel.Model.Novel; 19 | import com.tubes.lightnovel.Model.Volume; 20 | 21 | import java.util.List; 22 | 23 | public class NovelDetail extends AppCompatActivity { 24 | 25 | TextView sinopsis; 26 | ImageView imageNovel; 27 | 28 | RecyclerView recycleVolume; 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | setContentView(R.layout.activity_novel_detail); 33 | 34 | initialize(); 35 | //set image novel 36 | Log.i("besar volume", ""+Common.novelSelected.getVolume().size()); 37 | Picasso.get().load(Common.novelSelected.getImage()).into(imageNovel); 38 | 39 | //set sinopsis 40 | sinopsis.setText(Common.novelSelected.getSinopsis()); 41 | 42 | //set adapter 43 | onNovelLoadDoneListener(Common.novelSelected.getVolume()); 44 | } 45 | 46 | private void initialize() { 47 | sinopsis = (TextView) findViewById(R.id.sinopsis); 48 | imageNovel = (ImageView) findViewById(R.id.novel_Image); 49 | 50 | recycleVolume = (RecyclerView) findViewById(R.id.recycler_volume); 51 | recycleVolume.setHasFixedSize(true); 52 | recycleVolume.setLayoutManager(new GridLayoutManager(this,2)); 53 | 54 | 55 | } 56 | 57 | public void onNovelLoadDoneListener(List volumeList) { 58 | recycleVolume.setAdapter(new VolumeListAdapter(getBaseContext(), volumeList)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/ProfileActivity.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | import androidx.appcompat.app.AppCompatActivity; 6 | 7 | import android.content.Intent; 8 | import android.graphics.Bitmap; 9 | import android.net.Uri; 10 | import android.os.Bundle; 11 | import android.provider.MediaStore; 12 | import android.util.Log; 13 | import android.view.View; 14 | import android.widget.Button; 15 | import android.widget.ImageView; 16 | import android.widget.ProgressBar; 17 | import android.widget.Toast; 18 | 19 | import com.google.android.gms.tasks.OnCompleteListener; 20 | import com.google.android.gms.tasks.OnFailureListener; 21 | import com.google.android.gms.tasks.OnSuccessListener; 22 | import com.google.android.gms.tasks.Task; 23 | import com.google.android.material.textfield.TextInputLayout; 24 | import com.google.firebase.auth.FirebaseAuth; 25 | import com.google.firebase.auth.FirebaseUser; 26 | import com.google.firebase.auth.UserProfileChangeRequest; 27 | import com.google.firebase.storage.FirebaseStorage; 28 | import com.google.firebase.storage.StorageReference; 29 | import com.google.firebase.storage.UploadTask; 30 | import com.squareup.picasso.Picasso; 31 | import com.tubes.lightnovel.Common.Common; 32 | 33 | import java.io.IOException; 34 | 35 | public class ProfileActivity extends AppCompatActivity { 36 | 37 | private static final int CHOOSE_IMAGE = 101; 38 | TextInputLayout displayName; 39 | Button save, logOut; 40 | ImageView imageProfile; 41 | private Uri uriProfileImage; 42 | private ProgressBar progressBar; 43 | private String profileImageUrl; 44 | private FirebaseAuth mAuth; 45 | @Override 46 | protected void onCreate(Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | setContentView(R.layout.activity_profile); 49 | displayName = (TextInputLayout) findViewById(R.id.editTextDisplayName); 50 | save = (Button) findViewById(R.id.save_name); 51 | imageProfile = (ImageView) findViewById(R.id.imageView); 52 | progressBar = (ProgressBar) findViewById(R.id.progressbar); 53 | logOut = (Button) findViewById(R.id.log_out); 54 | //database reference 55 | 56 | mAuth = FirebaseAuth.getInstance(); 57 | 58 | loadProfileDetail(); 59 | 60 | imageProfile.setOnClickListener(new View.OnClickListener() { 61 | @Override 62 | public void onClick(View v) { 63 | showImageChooser(); 64 | } 65 | }); 66 | 67 | save.setOnClickListener(new View.OnClickListener() { 68 | @Override 69 | public void onClick(View v) { 70 | saveUserInformation(); 71 | } 72 | }); 73 | 74 | logOut.setOnClickListener(new View.OnClickListener() { 75 | @Override 76 | public void onClick(View v) { 77 | FirebaseAuth.getInstance().signOut(); 78 | finish(); 79 | startActivity(new Intent(ProfileActivity.this, MainActivity.class)); 80 | } 81 | }); 82 | 83 | 84 | } 85 | 86 | private void loadProfileDetail() { 87 | String display, photoUrl; 88 | photoUrl= ""; 89 | if(mAuth.getCurrentUser()!=null){ 90 | 91 | display = mAuth.getCurrentUser().getDisplayName(); 92 | if(mAuth.getCurrentUser().getPhotoUrl()!=null){ 93 | photoUrl = mAuth.getCurrentUser().getPhotoUrl().toString(); 94 | } 95 | 96 | if(display!=null && !photoUrl.equals("")){ 97 | 98 | displayName.getEditText().setText(display); 99 | Log.i("photoURL", ""+photoUrl); 100 | Picasso.get().load(photoUrl).into(imageProfile); 101 | } 102 | } 103 | 104 | 105 | } 106 | 107 | private void saveUserInformation() { 108 | 109 | 110 | String display = displayName.getEditText().getText().toString(); 111 | 112 | if (display.isEmpty()) { 113 | displayName.setError("Name required"); 114 | displayName.requestFocus(); 115 | return; 116 | } 117 | 118 | 119 | Log.i("update profile : ", display+profileImageUrl); 120 | 121 | if (mAuth.getCurrentUser() != null && profileImageUrl != null) { 122 | UserProfileChangeRequest profile = new UserProfileChangeRequest.Builder() 123 | .setDisplayName(display) 124 | .setPhotoUri(Uri.parse(profileImageUrl)) 125 | .build(); 126 | 127 | mAuth.getCurrentUser().updateProfile(profile) 128 | .addOnCompleteListener(new OnCompleteListener() { 129 | @Override 130 | public void onComplete(@NonNull Task task) { 131 | if (task.isSuccessful()) { 132 | Toast.makeText(ProfileActivity.this, "Profile Updated", Toast.LENGTH_SHORT).show(); 133 | } 134 | } 135 | }); 136 | } 137 | } 138 | 139 | @Override 140 | protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 141 | super.onActivityResult(requestCode, resultCode, data); 142 | 143 | if (requestCode == CHOOSE_IMAGE && resultCode == RESULT_OK && data != null && data.getData() != null) { 144 | uriProfileImage = data.getData(); 145 | try { 146 | Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uriProfileImage); 147 | imageProfile.setImageBitmap(bitmap); 148 | 149 | uploadImageToFirebaseStorage(); 150 | 151 | } catch (IOException e) { 152 | e.printStackTrace(); 153 | } 154 | } 155 | 156 | } 157 | 158 | private void uploadImageToFirebaseStorage() { 159 | final StorageReference profileImageRef = 160 | FirebaseStorage.getInstance().getReference("profilepics/" + mAuth.getCurrentUser().getUid() + ".jpg"); 161 | 162 | if (uriProfileImage != null) { 163 | progressBar.setVisibility(View.VISIBLE); 164 | profileImageRef.putFile(uriProfileImage) 165 | .addOnSuccessListener(new OnSuccessListener() { 166 | @Override 167 | public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { 168 | progressBar.setVisibility(View.GONE); 169 | profileImageUrl = taskSnapshot.getMetadata().getReference().getDownloadUrl().toString(); 170 | 171 | profileImageRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener() { 172 | @Override 173 | public void onSuccess(Uri uri) { 174 | profileImageUrl = uri.toString(); 175 | } 176 | }); 177 | 178 | } 179 | }) 180 | .addOnFailureListener(new OnFailureListener() { 181 | @Override 182 | public void onFailure(@NonNull Exception e) { 183 | progressBar.setVisibility(View.GONE); 184 | Toast.makeText(ProfileActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show(); 185 | } 186 | }); 187 | } 188 | } 189 | 190 | private void showImageChooser() { 191 | Intent intent = new Intent(); 192 | intent.setType("image/*"); 193 | intent.setAction(Intent.ACTION_GET_CONTENT); 194 | startActivityForResult(Intent.createChooser(intent, "Select Profile Image"), CHOOSE_IMAGE); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/Service/CheckForSDCard.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Service; 2 | 3 | import android.os.Environment; 4 | 5 | public class CheckForSDCard { 6 | //Check If SD Card is present or not method 7 | public boolean isSDCardPresent() { 8 | if (Environment.getExternalStorageState().equals( 9 | Environment.MEDIA_MOUNTED)) { 10 | return true; 11 | } 12 | return false; 13 | } 14 | } -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/Service/DownloadTask.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Service; 2 | 3 | import android.app.AlertDialog; 4 | import android.app.ProgressDialog; 5 | import android.content.ActivityNotFoundException; 6 | import android.content.Context; 7 | import android.content.DialogInterface; 8 | import android.content.Intent; 9 | import android.net.Uri; 10 | import android.os.AsyncTask; 11 | import android.os.Environment; 12 | import android.os.Handler; 13 | import android.util.Log; 14 | import android.view.ContextThemeWrapper; 15 | import android.widget.Toast; 16 | 17 | import com.folioreader.Config; 18 | import com.folioreader.model.locators.ReadLocator; 19 | import com.folioreader.util.ReadLocatorListener; 20 | import com.tubes.lightnovel.R; 21 | 22 | import java.io.File; 23 | import java.io.FileOutputStream; 24 | import java.io.InputStream; 25 | import java.net.HttpURLConnection; 26 | import java.net.URL; 27 | 28 | public class DownloadTask { 29 | private static final String TAG = "Download Task"; 30 | private Context context; 31 | 32 | private String downloadUrl, downloadFileName; 33 | private ProgressDialog progressDialog; 34 | 35 | public DownloadTask(Context context, String downloadUrl, String fileName) { 36 | this.context = context; 37 | 38 | this.downloadUrl = downloadUrl; 39 | Log.i("panjang url", downloadUrl+""+downloadUrl.length()); 40 | 41 | downloadFileName = fileName; 42 | Log.i(TAG, downloadFileName); 43 | 44 | //Start Downloading Task 45 | new DownloadingTask().execute(); 46 | } 47 | 48 | private class DownloadingTask extends AsyncTask { 49 | 50 | File apkStorage = null; 51 | File outputFile = null; 52 | 53 | @Override 54 | protected void onPreExecute() { 55 | super.onPreExecute(); 56 | progressDialog = new ProgressDialog(context); 57 | progressDialog.setMessage("Downloading..."); 58 | progressDialog.setCancelable(false); 59 | progressDialog.show(); 60 | } 61 | 62 | @Override 63 | protected void onPostExecute(Void result) { 64 | try { 65 | if (outputFile != null) { 66 | progressDialog.dismiss(); 67 | ContextThemeWrapper ctw = new ContextThemeWrapper( context, R.style.Reader); 68 | final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ctw); 69 | alertDialogBuilder.setTitle("Download "); 70 | alertDialogBuilder.setMessage("Novel Downloaded Successfully "); 71 | alertDialogBuilder.setCancelable(false); 72 | alertDialogBuilder.setPositiveButton("OK", new DialogInterface.OnClickListener() { 73 | public void onClick(DialogInterface dialog, int id) { 74 | 75 | } 76 | }); 77 | 78 | alertDialogBuilder.setNegativeButton("Open Book",new DialogInterface.OnClickListener() { 79 | public void onClick(DialogInterface dialog, int id) { 80 | String pdfFile = Environment.getExternalStorageDirectory() + "/LightNovel/" + downloadFileName; // -> filename = maven.pdf 81 | Log.i("Downloaded ",""+pdfFile); 82 | try{ 83 | new FolioReader(pdfFile, downloadFileName); 84 | }catch(ActivityNotFoundException e){ 85 | Toast.makeText(context, "Failed to launch Reader", Toast.LENGTH_SHORT).show(); 86 | } 87 | } 88 | }); 89 | alertDialogBuilder.show(); 90 | // Toast.makeText(context, "Document Downloaded Successfully", Toast.LENGTH_SHORT).show(); 91 | } else { 92 | 93 | new Handler().postDelayed(new Runnable() { 94 | @Override 95 | public void run() { 96 | 97 | } 98 | }, 3000); 99 | 100 | Log.e(TAG, "Download Failed"); 101 | 102 | } 103 | } catch (Exception e) { 104 | e.printStackTrace(); 105 | 106 | //Change button text if exception occurs 107 | 108 | new Handler().postDelayed(new Runnable() { 109 | @Override 110 | public void run() { 111 | 112 | } 113 | }, 3000); 114 | Log.e(TAG, "Download Failed with Exception - " + e.getLocalizedMessage()); 115 | 116 | } 117 | 118 | 119 | super.onPostExecute(result); 120 | } 121 | 122 | @Override 123 | protected Void doInBackground(Void... arg0) { 124 | try { 125 | URL url = new URL(downloadUrl);//Create Download URl 126 | HttpURLConnection c = (HttpURLConnection) url.openConnection();//Open Url Connection 127 | c.setRequestMethod("GET");//Set Request Method to "GET" since we are grtting data 128 | c.connect();//connect the URL Connection 129 | 130 | //If Connection response is not OK then show Logs 131 | if (c.getResponseCode() != HttpURLConnection.HTTP_OK) { 132 | Log.e(TAG, "Server returned HTTP " + c.getResponseCode() 133 | + " " + c.getResponseMessage()); 134 | 135 | } 136 | 137 | 138 | //Get File if SD card is present 139 | if (new CheckForSDCard().isSDCardPresent()) { 140 | 141 | apkStorage = new File(Environment.getExternalStorageDirectory() + "/" + "LightNovel"); 142 | } else 143 | Toast.makeText(context, "Oops!! There is no SD Card.", Toast.LENGTH_SHORT).show(); 144 | 145 | //If File is not present create directory 146 | if (!apkStorage.exists()) { 147 | apkStorage.mkdir(); 148 | Log.e(TAG, "Directory Created."); 149 | } 150 | 151 | outputFile = new File(apkStorage, downloadFileName);//Create Output file in Main File 152 | 153 | //Create New File if not present 154 | if (!outputFile.exists()) { 155 | outputFile.createNewFile(); 156 | Log.e(TAG, "File Created"); 157 | } 158 | 159 | FileOutputStream fos = new FileOutputStream(outputFile);//Get OutputStream for NewFile Location 160 | 161 | InputStream is = c.getInputStream();//Get InputStream for connection 162 | 163 | byte[] buffer = new byte[1024];//Set buffer type 164 | int len1 = 0;//init length 165 | while ((len1 = is.read(buffer)) != -1) { 166 | fos.write(buffer, 0, len1);//Write new file 167 | } 168 | 169 | //Close all connection after doing task 170 | fos.close(); 171 | is.close(); 172 | 173 | } catch (Exception e) { 174 | 175 | //Read exception if something went wrong 176 | e.printStackTrace(); 177 | outputFile = null; 178 | Log.e(TAG, "Download Error Exception " + e.getMessage()); 179 | } 180 | 181 | return null; 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/Service/FolioReader.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Service; 2 | 3 | import android.util.Log; 4 | import android.widget.Toast; 5 | 6 | import com.folioreader.Config; 7 | import com.folioreader.model.locators.ReadLocator; 8 | import com.folioreader.util.ReadLocatorListener; 9 | import com.google.firebase.auth.FirebaseAuth; 10 | import com.google.firebase.database.DataSnapshot; 11 | import com.google.firebase.database.DatabaseError; 12 | import com.google.firebase.database.DatabaseReference; 13 | import com.google.firebase.database.FirebaseDatabase; 14 | import com.google.firebase.database.ValueEventListener; 15 | import com.tubes.lightnovel.R; 16 | 17 | public class FolioReader { 18 | private com.folioreader.FolioReader fReader; 19 | private Config config; 20 | private FirebaseDatabase database; 21 | private DatabaseReference myRef; 22 | private FirebaseAuth mAuth; 23 | private String path, fileName; 24 | 25 | public FolioReader(String path, String downloadFileName) { 26 | this.path = path; 27 | this.fileName = downloadFileName; 28 | initialize(); 29 | } 30 | 31 | private void initialize() { 32 | //database 33 | database = FirebaseDatabase.getInstance(); 34 | 35 | mAuth = FirebaseAuth.getInstance(); 36 | 37 | 38 | fileName = fileName.substring(0,fileName.length()-5); 39 | Log.i("nama file", fileName); 40 | 41 | //Reader 42 | fReader = com.folioreader.FolioReader.get(); 43 | Config config = new Config(); 44 | config.setNightMode(true); 45 | config.setThemeColorRes(R.color.colorAccent); 46 | config.setAllowedDirection(Config.AllowedDirection.VERTICAL_AND_HORIZONTAL); 47 | config.setDirection(Config.Direction.VERTICAL); 48 | config.setShowTts(false); 49 | fReader.setConfig(config,true); 50 | 51 | if(mAuth.getCurrentUser()!=null) { 52 | myRef = database.getReference("UserLocator/ReadLocator/"+ mAuth.getCurrentUser().getUid()); 53 | checkSavedLocation(); 54 | } 55 | else{ 56 | fReader.openBook(path); 57 | } 58 | 59 | 60 | 61 | 62 | 63 | fReader.setReadLocatorListener(new ReadLocatorListener() { 64 | @Override 65 | public void saveReadLocator(ReadLocator readLocator) { 66 | 67 | Log.i("message", "-> saveReadLocator -> " + readLocator.toJson()); 68 | if(mAuth.getCurrentUser()!=null) { 69 | myRef = database.getReference("UserLocator/ReadLocator/" + mAuth.getCurrentUser().getUid()); 70 | myRef.child(fileName).setValue(readLocator); 71 | } 72 | // Toast.makeText(fReader.getClass().getconte, "Failed to launch Reader", Toast.LENGTH_SHORT).show(); 73 | //fReader.close(); 74 | /*ReadLocator has toJson() method which gives JSON in following format - 75 | { 76 | bookId : string, 77 | href : string, 78 | created : integer, 79 | locations : { 80 | cfi : string 81 | } 82 | } 83 | You can save this last read position in your local or remote db*/ 84 | } 85 | }); 86 | } 87 | 88 | private void checkSavedLocation() { 89 | myRef = database.getReference("UserLocator/ReadLocator/"+ mAuth.getCurrentUser().getUid()+"/"+fileName); 90 | myRef.addListenerForSingleValueEvent(new ValueEventListener() { 91 | @Override 92 | public void onDataChange(DataSnapshot dataSnapshot) { 93 | ReadLocator post = dataSnapshot.getValue(ReadLocator.class); 94 | if(post!=null){ 95 | Log.i("message", "-> hasil database -> " + post.toJson()); 96 | fReader.setReadLocator(post); 97 | } 98 | fReader.openBook(path); 99 | } 100 | 101 | @Override 102 | public void onCancelled(DatabaseError databaseError) { 103 | System.out.println("The read failed: " + databaseError.getCode()); 104 | } 105 | }); 106 | 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/Service/PicassoLoadingService.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel.Service; 2 | 3 | import android.widget.ImageView; 4 | 5 | import com.squareup.picasso.Picasso; 6 | 7 | import ss.com.bannerslider.ImageLoadingService; 8 | 9 | public class PicassoLoadingService implements ImageLoadingService { 10 | @Override 11 | public void loadImage(String url, ImageView imageView) { 12 | Picasso.get().load(url).into(imageView); 13 | } 14 | 15 | @Override 16 | public void loadImage(int resource, ImageView imageView) { 17 | Picasso.get().load(resource).into(imageView); 18 | } 19 | 20 | @Override 21 | public void loadImage(String url, int placeHolder, int errorDrawable, ImageView imageView) { 22 | Picasso.get().load(url).placeholder(placeHolder).error(errorDrawable).into(imageView); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/SignUp.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.appcompat.app.ActionBar; 5 | import androidx.appcompat.app.AppCompatActivity; 6 | 7 | import android.app.ActivityOptions; 8 | import android.content.Intent; 9 | import android.os.Bundle; 10 | import android.os.PatternMatcher; 11 | import android.util.Log; 12 | import android.util.Pair; 13 | import android.util.Patterns; 14 | import android.view.View; 15 | import android.widget.Button; 16 | import android.widget.EditText; 17 | import android.widget.ImageView; 18 | import android.widget.TextView; 19 | import android.widget.Toast; 20 | 21 | import com.google.android.gms.tasks.OnCompleteListener; 22 | import com.google.android.gms.tasks.Task; 23 | import com.google.android.material.textfield.TextInputEditText; 24 | import com.google.android.material.textfield.TextInputLayout; 25 | import com.google.firebase.auth.AuthResult; 26 | import com.google.firebase.auth.FirebaseAuth; 27 | import com.google.firebase.auth.FirebaseAuthUserCollisionException; 28 | 29 | public class SignUp extends AppCompatActivity { 30 | Button callSignUp,signUp; 31 | ImageView image; 32 | TextView logoText, sloganText; 33 | TextInputLayout username,password,repassword; 34 | 35 | private FirebaseAuth mAuth; 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_sign_up); 40 | ActionBar actionBar = getSupportActionBar(); 41 | actionBar.hide(); 42 | callSignUp = findViewById(R.id.go_login_screen); 43 | image = findViewById(R.id.Logo_image); 44 | logoText = findViewById(R.id.Logo_name); 45 | sloganText = findViewById(R.id.slogan_name); 46 | username = findViewById(R.id.email_s); 47 | password = findViewById(R.id.password_s); 48 | repassword = findViewById(R.id.re_password_s); 49 | signUp = findViewById(R.id.Sign_up); 50 | 51 | //firebase auth 52 | mAuth = FirebaseAuth.getInstance(); 53 | 54 | callSignUp.setOnClickListener(new View.OnClickListener() { 55 | @Override 56 | public void onClick(View view) { 57 | onBackPressed(); 58 | } 59 | }); 60 | 61 | signUp.setOnClickListener(new View.OnClickListener(){ 62 | 63 | @Override 64 | public void onClick(View v) { 65 | registerUser(); 66 | } 67 | }); 68 | } 69 | 70 | private void registerUser() { 71 | String email = username.getEditText().getText().toString().trim(); 72 | String pass = password.getEditText().getText().toString().trim(); 73 | String pass2 = repassword.getEditText().getText().toString().trim(); 74 | if(email.isEmpty()){ 75 | username.setError("Email is required"); 76 | username.requestFocus(); 77 | return; 78 | } 79 | Log.i("Email", "fsdafsd"+email); 80 | if(!Patterns.EMAIL_ADDRESS.matcher(email).matches()){ 81 | username.setError("Please enter a valid email address"); 82 | username.requestFocus(); 83 | return; 84 | } 85 | 86 | if(pass.isEmpty()){ 87 | password.setError("Password is required"); 88 | password.requestFocus(); 89 | return; 90 | } 91 | 92 | if(pass.length()<6){ 93 | password.setError("Minimum length of password is 6"); 94 | password.requestFocus(); 95 | return; 96 | } 97 | 98 | if(!pass.equals(pass2)){ 99 | password.setError("Password did not match"); 100 | password.requestFocus(); 101 | return; 102 | } 103 | mAuth.createUserWithEmailAndPassword(email, pass).addOnCompleteListener(new OnCompleteListener() { 104 | @Override 105 | public void onComplete(@NonNull Task task) { 106 | if(task.isSuccessful()){ 107 | onBackPressed(); 108 | Toast.makeText(getApplicationContext(),"Sign Up Succesful", Toast.LENGTH_LONG).show(); 109 | } 110 | else{ 111 | 112 | if(task.getException() instanceof FirebaseAuthUserCollisionException){ 113 | Toast.makeText(getApplicationContext(),"You're Already Registered", Toast.LENGTH_LONG).show(); 114 | } 115 | else{ 116 | Toast.makeText(getApplicationContext(),task.getException().getMessage(), Toast.LENGTH_LONG).show(); 117 | } 118 | 119 | } 120 | } 121 | }); 122 | 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /app/src/main/java/com/tubes/lightnovel/SplashScreen.java: -------------------------------------------------------------------------------- 1 | package com.tubes.lightnovel; 2 | 3 | import androidx.appcompat.app.ActionBar; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | 6 | import android.app.ActivityOptions; 7 | import android.content.Intent; 8 | import android.os.Bundle; 9 | import android.os.Handler; 10 | import android.util.Pair; 11 | import android.view.View; 12 | import android.view.WindowManager; 13 | import android.view.animation.Animation; 14 | import android.view.animation.AnimationUtils; 15 | import android.widget.ImageView; 16 | import android.widget.TextView; 17 | import android.widget.Toast; 18 | 19 | import com.google.firebase.auth.FirebaseAuth; 20 | 21 | public class SplashScreen extends AppCompatActivity { 22 | 23 | private static int SPLASH_SCREEN = 2500; 24 | //variables 25 | Animation topAnim; 26 | Animation bottomAnim; 27 | ImageView image; 28 | TextView logo, slogan; 29 | //database reference 30 | FirebaseAuth mAuth; 31 | @Override 32 | protected void onCreate(Bundle savedInstanceState) { 33 | super.onCreate(savedInstanceState); 34 | getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN); 35 | setContentView(R.layout.activity_splash_screen); 36 | 37 | //database reference 38 | mAuth = FirebaseAuth.getInstance(); 39 | //Animations 40 | topAnim = AnimationUtils.loadAnimation(this,R.anim.top_animation); 41 | bottomAnim = AnimationUtils.loadAnimation(this,R.anim.bottom_animation); 42 | 43 | //hooks 44 | image = findViewById(R.id.imageView); 45 | logo = findViewById(R.id.textView2); 46 | slogan = findViewById(R.id.textView3); 47 | 48 | image.setAnimation(topAnim); 49 | logo.setAnimation(bottomAnim); 50 | slogan.setAnimation(bottomAnim); 51 | ActionBar actionBar = getSupportActionBar(); 52 | actionBar.hide(); 53 | new Handler().postDelayed(new Runnable() { 54 | @Override 55 | public void run() { 56 | if(mAuth.getCurrentUser()!=null){ 57 | Intent intent = new Intent(SplashScreen.this, MainActivity.class); 58 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 59 | startActivity(intent); 60 | finish(); 61 | Toast.makeText(getApplicationContext(),"Welcome Fellow Daoist", Toast.LENGTH_LONG).show(); 62 | } 63 | else { 64 | Intent intent = new Intent(SplashScreen.this, Login.class); 65 | 66 | Pair[] pairs = new Pair[2]; 67 | pairs[0] = new Pair(image, "logo_image"); 68 | pairs[1] = new Pair(logo, "logo_text"); 69 | 70 | ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(SplashScreen.this, pairs); 71 | startActivity(intent, options.toBundle()); 72 | finish(); 73 | } 74 | } 75 | },SPLASH_SCREEN); 76 | 77 | } 78 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/bottom_animation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/anim/top_animation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi/account.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi/add_profile.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi/close_chip.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi/ic_search.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-anydpi/menu_search.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-hdpi/account.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/add_profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-hdpi/add_profile.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/close_chip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-hdpi/close_chip.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-hdpi/ic_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/menu_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-hdpi/menu_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-mdpi/account.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/add_profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-mdpi/add_profile.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/close_chip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-mdpi/close_chip.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-mdpi/ic_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-mdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/menu_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-mdpi/menu_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/font_no.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-v24/font_no.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-xhdpi/account.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/add_profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-xhdpi/add_profile.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/close_chip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-xhdpi/close_chip.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-xhdpi/ic_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/menu_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-xhdpi/menu_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-xxhdpi/account.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/add_profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-xxhdpi/add_profile.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/close_chip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-xxhdpi/close_chip.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-xxhdpi/ic_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/menu_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilhamrecca/HikariNovel/d0b48f3f6e71b01671134fb1549602f910cc72ac/app/src/main/res/drawable-xxhdpi/menu_search.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/separator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/font/antic.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/font/anton.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/font/bungee.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_filter_search.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 15 | 16 | 22 | 23 | 32 | 33 | 34 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 18 | 19 | 29 | 30 | 38 | 39 | 45 | 46 | 53 | 54 | 57 | 58 | 59 | 60 | 68 | 69 | 74 | 75 | 76 | 77 | 78 |