├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── dictionaries │ └── ice.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── top │ │ └── wefor │ │ └── randompicker │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── top │ │ │ └── wefor │ │ │ └── randompicker │ │ │ ├── EntranceActivity.kt │ │ │ ├── MainActivity.java │ │ │ ├── Music.java │ │ │ ├── PlayListActivity.java │ │ │ ├── SnackbarUtil.kt │ │ │ ├── game │ │ │ ├── DiceActivity.kt │ │ │ ├── GameActivity.kt │ │ │ └── ScoreShuffleActivity.kt │ │ │ └── music │ │ │ ├── MusicActivity.kt │ │ │ ├── MusicBean.kt │ │ │ ├── MusicPlayer.kt │ │ │ └── MusicProvider.kt │ └── res │ │ ├── drawable-hdpi │ │ ├── ic_attack.gif │ │ ├── ic_critical.gif │ │ └── ic_unknown.png │ │ ├── drawable-xxhdpi │ │ └── img_disco.png │ │ ├── drawable │ │ ├── ic_check_red_600_48dp.xml │ │ ├── ic_content_cut_36dp.xml │ │ ├── ic_content_cut_36dp_checked.xml │ │ ├── ic_music_note_white_36dp.xml │ │ ├── ic_playlist_add_check_white_24dp.xml │ │ ├── ic_playlist_play_white_24dp.xml │ │ ├── ic_skip_next_36dp_pressed.xml │ │ ├── ic_skip_next_white_36dp.xml │ │ ├── ic_skip_previous_36dp_pressed.xml │ │ ├── ic_skip_previous_white_36dp.xml │ │ ├── selector_cut_checkbox.xml │ │ ├── selector_next_btn.xml │ │ └── selector_previous_btn.xml │ │ ├── layout │ │ ├── activity_dice.xml │ │ ├── activity_entrance.xml │ │ ├── activity_game.xml │ │ ├── activity_main.xml │ │ ├── activity_music.xml │ │ ├── activity_play_list.xml │ │ ├── activity_score_shuffle.xml │ │ └── item_play_list.xml │ │ ├── menu │ │ └── main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ ├── values-zh │ │ └── strings.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── top │ └── wefor │ └── randompicker │ └── ExampleUnitTest.java ├── build.gradle ├── doc └── Random and Game(博弈).md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── lib ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── top │ └── wefor │ └── randompicker │ ├── Calculator.java │ ├── CutMode.java │ ├── IncrementCalculator.java │ ├── RandomList.java │ ├── RandomPicker.java │ ├── SkipMode.java │ └── util │ └── Sampling.java ├── projectFilesBackup └── .idea │ └── workspace.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .DS_Store 5 | /build 6 | /captures 7 | /.idea/ 8 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | RandomPicker -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/dictionaries/ice.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 23 | 33 | 34 | 35 | 36 | 37 | 38 | 40 | 41 | 42 | 43 | 44 | 45 | 50 | 51 | 52 | 53 | 54 | 55 | 1.8 56 | 57 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 Wefor 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RandomPicker [![](https://jitpack.io/v/XunMengWinter/RandomPicker.svg)](https://jitpack.io/#XunMengWinter/RandomPicker) 2 | 3 | [简书中文](https://www.jianshu.com/p/4c28269e6203) 4 | 5 | ### Idea:💡 6 | Pseudo-random. There are some songs in your play list, each song's weight is 1 in the first. 7 | Each time you click the next button, each song's weight increases by 1, and the selected song's weight returns to 0. 8 | The song's weight is positively correlated with the chance of being picked. 9 | 10 | | - | Love Story | 东风破 |Refrain | Tassel| - | 11 | | -------|:----------:| -------:| -------:|------:|-----------:| 12 | |sequence| weight | weight |weight | weight| picked | 13 | | 1 | 1 | 1 | 1 | 1 | 东风破 | 14 | | 2 | 2 | 0 | 2 | 2 | Love Story | 15 | | 3 | 0 | 1 | 3 | 3 | Refrain | 16 | | 4 | 1 | 2 | 0 | 4 | Tassel | 17 | | 5 | 2 | 3 | 1 | 0 | Love Story | 18 | | 6 | 0 | 4 | 2 | 1 | Tassel | 19 | | 7 | 1 | 5 | 3 | 0 | 东风破 | 20 | | 8 | 2 | 0 | 4 | 1 | Love Story | 21 | | 9 | 0 | 1 | 5 | 2 | Tassel | 22 | | 10 | 1 | 2 | 6 | 0 | ... | 23 | ... 24 | 25 | ### Demo 26 | ![RandomPicker](https://raw.githubusercontent.com/XunMengWinter/source/master/images/RandomPicker.jpg) 27 | 28 | ### How to use 29 | Quick start: 30 | ``` 31 | RandomPicker randomPicker = new RandomPicker(12); 32 | int nextPos = randomPicker.next(); 33 | ``` 34 | More function: 35 | ``` 36 | randomPicker.setMultiplyNumber(3); 37 | randomPicker.setAddNumber(2); 38 | randomPicker.setNextPick(5); 39 | randomPicker.add(); 40 | randomPicker.changeOriginWeight(0,3); 41 | randomPicker.getHistoryList(); 42 | ``` 43 | More functions: 44 | [download this repo and take a look at the code.](https://github.com/XunMengWinter/RandomPicker) 45 | 46 | ### Compile 47 | The latest version [![](https://jitpack.io/v/XunMengWinter/RandomPicker.svg)](https://jitpack.io/#XunMengWinter/RandomPicker) 48 | 49 | add this to the project level build.gradle file 50 | 51 | ``` 52 | allprojects { 53 | repositories { 54 | ... 55 | maven { url "https://jitpack.io" } 56 | } 57 | } 58 | ``` 59 | 60 | add the dependency to the app level build.gradle file 61 | 62 | ``` 63 | // replace {x.y.z} with the latest version. 64 | compile 'com.github.XunMengWinter:RandomPicker:{x.y.z}' 65 | ``` 66 | 67 | 68 | ----------------- 69 | p.s. If you have any suggestions for improvement, please discuss on issues or pull requests. 70 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | 4 | android { 5 | compileSdkVersion 27 6 | buildToolsVersion '27.0.3' 7 | 8 | defaultConfig { 9 | applicationId "top.wefor.randompicker" 10 | minSdkVersion 14 11 | targetSdkVersion 27 12 | versionCode 3 13 | versionName "1.2" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | 22 | } 23 | 24 | dependencies { 25 | implementation fileTree(include: ['*.jar'], dir: 'libs') 26 | implementation 'com.android.support.constraint:constraint-layout:1.1.2' 27 | testImplementation 'junit:junit:4.12' 28 | implementation 'com.android.support:appcompat-v7:27.1.1' 29 | implementation 'com.android.support:design:27.1.1' 30 | 31 | implementation project(':lib') 32 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 33 | } 34 | repositories { 35 | mavenCentral() 36 | } 37 | 38 | apply plugin: 'kotlin-android-extensions' 39 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/ice/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/top/wefor/randompicker/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package top.wefor.randompicker; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 23 | 26 | 29 | 32 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/java/top/wefor/randompicker/EntranceActivity.kt: -------------------------------------------------------------------------------- 1 | package top.wefor.randompicker 2 | 3 | import android.content.Intent 4 | import android.support.v7.app.AppCompatActivity 5 | import android.os.Bundle 6 | import android.view.View 7 | import top.wefor.randompicker.game.DiceActivity 8 | import top.wefor.randompicker.game.GameActivity 9 | 10 | class EntranceActivity : AppCompatActivity() { 11 | 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | setContentView(R.layout.activity_entrance) 15 | } 16 | 17 | fun music(view: View){ 18 | startActivity(Intent(this, MainActivity::class.java)) 19 | } 20 | 21 | fun game(view: View){ 22 | startActivity(Intent(this, DiceActivity::class.java)) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/top/wefor/randompicker/MainActivity.java: -------------------------------------------------------------------------------- 1 | package top.wefor.randompicker; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AlertDialog; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.Menu; 8 | import android.view.MenuItem; 9 | import android.view.View; 10 | import android.widget.CheckBox; 11 | import android.widget.CompoundButton; 12 | 13 | import java.util.ArrayList; 14 | 15 | import top.wefor.randompicker.music.MusicActivity; 16 | 17 | public class MainActivity extends AppCompatActivity { 18 | 19 | RandomPicker mRandomPicker; 20 | ArrayList mMusicList = new ArrayList<>(); 21 | ArrayList mHistoryList = new ArrayList<>(); 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_main); 27 | 28 | mMusicList.add(new Music("东风破")); 29 | mMusicList.add(new Music("Valder Fields")); 30 | mMusicList.add(new Music("Refrain")); 31 | mMusicList.add(new Music("花落暮夕")); 32 | mMusicList.add(new Music("Flower Dance")); 33 | mMusicList.add(new Music("Love Story")); 34 | mMusicList.add(new Music("Neptune Illusion")); 35 | mMusicList.add(new Music("My Soul")); 36 | mMusicList.add(new Music("Dark of Night")); 37 | mMusicList.add(new Music("猎户星座")); 38 | mMusicList.add(new Music("Better Off")); 39 | mMusicList.add(new Music("告白气球")); 40 | mMusicList.add(new Music("Tassel")); 41 | mMusicList.add(new Music("Gotta Have You")); 42 | mMusicList.add(new Music("白金ディスコ")); 43 | mMusicList.add(new Music("且听风吟")); 44 | 45 | mMusicList.get(0).weight = 3; 46 | mMusicList.get(mMusicList.size() - 1).weight = 5; 47 | 48 | 49 | mRandomPicker = new RandomPicker(mMusicList.size(), 1); 50 | // mRandomPicker.setRepeatable(true); 51 | mRandomPicker.setCalculator(new Calculator() { 52 | @Override 53 | public int calculateNextWeight(int position, int currentWeight, int originWeight) { 54 | return (currentWeight + 1) * originWeight; 55 | } 56 | }); 57 | 58 | mRandomPicker.changeOriginWeight(0, 3); 59 | mRandomPicker.changeOriginWeight(mMusicList.size() - 1, 5); 60 | mRandomPicker.setNextPick(3); 61 | 62 | findViewById(R.id.nextBtn_iv).setOnClickListener(new View.OnClickListener() { 63 | @Override 64 | public void onClick(View view) { 65 | int nextPos = mRandomPicker.next(); 66 | Music music = mMusicList.get(nextPos); 67 | setTitle(music.name); 68 | music.pickedTime++; 69 | mHistoryList.add(music); 70 | } 71 | }); 72 | 73 | CheckBox checkBox = (CheckBox) findViewById(R.id.cutMode_checkBox); 74 | checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 75 | @Override 76 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 77 | SnackbarUtilKt.showTips(buttonView, (isChecked ? "Enter" : "Exit") + " Cut mode"); 78 | 79 | if (isChecked) 80 | mRandomPicker.enterCutMode(); 81 | else 82 | mRandomPicker.exitCutMode(); 83 | } 84 | }); 85 | } 86 | 87 | @Override 88 | public boolean onCreateOptionsMenu(Menu menu) { 89 | getMenuInflater().inflate(R.menu.main, menu); 90 | return true; 91 | } 92 | 93 | @Override 94 | public boolean onOptionsItemSelected(MenuItem item) { 95 | switch (item.getItemId()) { 96 | case R.id.action_play_list: 97 | PlayListActivity.go(MainActivity.this, mMusicList); 98 | return true; 99 | case R.id.action_history_list: 100 | StringBuilder stringBuilder = new StringBuilder(); 101 | for (int i = 0; i < mHistoryList.size(); i++) { 102 | stringBuilder.append(mHistoryList.get(i).name).append("\n"); 103 | } 104 | new AlertDialog.Builder(MainActivity.this) 105 | .setTitle("Played List") 106 | .setMessage(stringBuilder) 107 | .create().show(); 108 | return true; 109 | } 110 | return super.onOptionsItemSelected(item); 111 | } 112 | 113 | public void openMusicAty(View view) { 114 | startActivity(new Intent(this, MusicActivity.class)); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /app/src/main/java/top/wefor/randompicker/Music.java: -------------------------------------------------------------------------------- 1 | package top.wefor.randompicker; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | /** 7 | * Created on 16/8/26. 8 | * 9 | * @author ice 10 | */ 11 | public class Music implements Parcelable { 12 | 13 | public String name = ""; 14 | public int weight = 1; 15 | public int pickedTime = 0; 16 | 17 | public Music(String name) { 18 | this.name = name; 19 | } 20 | 21 | 22 | @Override 23 | public int describeContents() { 24 | return 0; 25 | } 26 | 27 | @Override 28 | public void writeToParcel(Parcel dest, int flags) { 29 | dest.writeString(this.name); 30 | dest.writeInt(this.weight); 31 | dest.writeInt(this.pickedTime); 32 | } 33 | 34 | protected Music(Parcel in) { 35 | this.name = in.readString(); 36 | this.weight = in.readInt(); 37 | this.pickedTime = in.readInt(); 38 | } 39 | 40 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 41 | @Override 42 | public Music createFromParcel(Parcel source) { 43 | return new Music(source); 44 | } 45 | 46 | @Override 47 | public Music[] newArray(int size) { 48 | return new Music[size]; 49 | } 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/top/wefor/randompicker/PlayListActivity.java: -------------------------------------------------------------------------------- 1 | package top.wefor.randompicker; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.annotation.Nullable; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.view.MenuItem; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.TextView; 14 | 15 | import java.util.ArrayList; 16 | 17 | /** 18 | * Created on 16/8/26. 19 | * 20 | * @author ice 21 | */ 22 | public class PlayListActivity extends AppCompatActivity { 23 | private static final String PLAY_LIST = "play_list"; 24 | 25 | private ArrayList mMusics; 26 | 27 | public static void go(Context context, ArrayList historyList) { 28 | Intent intent = new Intent(context, PlayListActivity.class); 29 | intent.putParcelableArrayListExtra(PLAY_LIST, historyList); 30 | context.startActivity(intent); 31 | } 32 | 33 | @Override 34 | protected void onCreate(@Nullable Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | setContentView(R.layout.activity_play_list); 37 | if (getSupportActionBar() != null) { 38 | // 默认左上角按钮可以点击 39 | getSupportActionBar().setHomeButtonEnabled(true); 40 | // 默认显示左上角返回按钮 41 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 42 | } 43 | 44 | mMusics = getIntent().getParcelableArrayListExtra(PLAY_LIST); 45 | 46 | initList(); 47 | } 48 | 49 | @Override 50 | public boolean onOptionsItemSelected(MenuItem item) { 51 | if (item.getItemId() == android.R.id.home) { 52 | onBackPressed(); 53 | return true; 54 | } 55 | return super.onOptionsItemSelected(item); 56 | } 57 | 58 | private void initList() { 59 | RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView); 60 | recyclerView.setHasFixedSize(true); 61 | recyclerView.setLayoutManager(new LinearLayoutManager(this)); 62 | recyclerView.setAdapter(new RecyclerView.Adapter() { 63 | @Override 64 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 65 | final View view = getLayoutInflater().inflate(R.layout.item_play_list, parent, false); 66 | return new MyViewHolder(view); 67 | } 68 | 69 | @Override 70 | public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { 71 | MyViewHolder myViewHolder = (MyViewHolder) holder; 72 | Music music = mMusics.get(position); 73 | myViewHolder.musicTv.setText(music.name); 74 | myViewHolder.weightTv.setText(music.weight + ""); 75 | myViewHolder.pickedTimeTv.setText(music.pickedTime + ""); 76 | } 77 | 78 | @Override 79 | public int getItemCount() { 80 | return mMusics.size(); 81 | } 82 | 83 | class MyViewHolder extends RecyclerView.ViewHolder { 84 | 85 | private TextView musicTv, weightTv, pickedTimeTv; 86 | 87 | public MyViewHolder(View itemView) { 88 | super(itemView); 89 | musicTv = (TextView) itemView.findViewById(R.id.music_tv); 90 | weightTv = (TextView) itemView.findViewById(R.id.weight_tv); 91 | pickedTimeTv = (TextView) itemView.findViewById(R.id.pickedTime_tv); 92 | } 93 | } 94 | 95 | }); 96 | 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /app/src/main/java/top/wefor/randompicker/SnackbarUtil.kt: -------------------------------------------------------------------------------- 1 | package top.wefor.randompicker 2 | 3 | import android.support.design.widget.Snackbar 4 | import android.view.View 5 | 6 | /** 7 | * Created on 2018/7/28. 8 | * @author ice 9 | */ 10 | fun showTips(triggerView: View, text: CharSequence) { 11 | val snackbar = Snackbar.make(triggerView, text, 2000) 12 | val view = snackbar.view 13 | view.setBackgroundResource(R.color.colorPrimary) 14 | snackbar.show() 15 | } -------------------------------------------------------------------------------- /app/src/main/java/top/wefor/randompicker/game/DiceActivity.kt: -------------------------------------------------------------------------------- 1 | package top.wefor.randompicker.game 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import android.support.v7.app.AppCompatActivity 6 | import android.util.Log 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import android.widget.* 10 | import top.wefor.randompicker.Calculator 11 | import top.wefor.randompicker.IncrementCalculator 12 | import top.wefor.randompicker.R 13 | import top.wefor.randompicker.RandomPicker 14 | 15 | /** 16 | * 用 RandomPicker 实现各种随机机制 17 | */ 18 | class DiceActivity : AppCompatActivity() { 19 | 20 | private lateinit var mRadioGroup: RadioGroup 21 | private lateinit var mWeightLayout: ViewGroup 22 | private lateinit var mSpinner: Spinner 23 | private lateinit var mPercentTv: TextView 24 | private lateinit var mBoardTv: TextView 25 | private val mRandomPicker = RandomPicker() 26 | 27 | private val mCriticalPos = 0 28 | private var mTotal = 0 29 | private var mCritical = 0 30 | 31 | override fun onCreate(savedInstanceState: Bundle?) { 32 | super.onCreate(savedInstanceState) 33 | 34 | initView(savedInstanceState) 35 | } 36 | 37 | fun initView(savedInstanceState: Bundle?) { 38 | setContentView(R.layout.activity_dice) 39 | 40 | mRadioGroup = findViewById(R.id.candidate_layout) 41 | mWeightLayout = findViewById(R.id.weight_layout) 42 | mSpinner = findViewById(R.id.spinner) 43 | mPercentTv = findViewById(R.id.percent_tv) 44 | mBoardTv = findViewById(R.id.board_tv) 45 | 46 | val spinnerList = arrayListOf() 47 | spinnerList.add("1. originWeight++ random") //默认随机 48 | spinnerList.add("2. independent random") //独立随机。即"真随机" 49 | spinnerList.add("3. Blizzard War3 critical random") //war3暴击随机 50 | spinnerList.add("4. draw random: one by one") //翻牌随机:一次一张地翻 51 | 52 | val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, spinnerList) 53 | mSpinner.adapter = adapter 54 | mSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { 55 | override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { 56 | run { 57 | mRandomPicker.exitCutMode() //退出切歌模式 58 | when (position) { 59 | 0 -> setRandomPicker(IncrementCalculator()) 60 | 1 -> setRandomPicker(Calculator { position, currentWeight, originWeight -> 1 }) //实现独立随机比较简单,weight永远返回同一个常数即可。 61 | 2 -> setRandomPicker(Calculator { position, currentWeight, originWeight -> 62 | // war3随机有个特点:有几率触发100%暴击,但是不会出现0%暴击的情况。 63 | // 计算公式大致为,综合暴击率20%时:设定初始暴击几率5%,未触发暴击则暴击率翻倍,若触发暴击则重置为初始暴击几率。 64 | var weight = 0 65 | if (position == mCriticalPos) { 66 | if (currentWeight == 0)//说明需要初始化暴击几率 67 | weight = 5 68 | else 69 | weight = Math.min(currentWeight * 2, 100) 70 | } else { 71 | try { 72 | weight = (100 - mRandomPicker.getWeight(mCriticalPos)) / 4 73 | } catch (e: Exception) { 74 | weight = (100 - 5) / 4 75 | } 76 | } 77 | //这种情况,走RandomPicker有点多此一举(而且需要把特定数放在第一位),而且效率不高。不过好处在于接口统一。 78 | weight 79 | }) 80 | 3 -> { 81 | //随机翻牌,其实开启切歌模式即可。 82 | mRandomPicker.enterCutMode() 83 | setRandomPicker(Calculator { position, currentWeight, originWeight -> 1 }) 84 | } 85 | } 86 | } 87 | } 88 | 89 | override fun onNothingSelected(parent: AdapterView<*>) { 90 | 91 | } 92 | } 93 | mSpinner.setSelection(0) 94 | 95 | mBoardTv.setText("用 RandomPikcer 实现几种常见的随机机制" 96 | + "\n\n1.RandomPicker默认模式,被选中的权重变为0,未被选中的权重++。" 97 | + "\n\n2.独立随机,即游戏领域所说的'真随机',每次随机都不受之前结果影响。" 98 | + "\n\n3.暴雪war3暴击机制,综合暴击率20%时:设定初始暴击几率5%,未触发暴击则暴击率翻倍,若触发暴击则重置为初始暴击几率。" 99 | + "\n\n4.翻牌随机,一次翻一张牌。每一轮翻牌中,翻过的牌不会再出现。与音乐播放里的切歌模式一致。" 100 | ) 101 | } 102 | 103 | fun setRandomPicker(calculator: Calculator) { 104 | Log.i("xyz", "setRandomPicker " + calculator.calculateNextWeight(0, 0, 1)) 105 | mRandomPicker.setCalculator(calculator) 106 | mRandomPicker.setRepeatable(true) 107 | mRandomPicker.resetList(mRadioGroup.childCount, 1) 108 | 109 | mTotal = 0 110 | mCritical = 0 111 | showCriticalTv() 112 | } 113 | 114 | fun showCriticalTv() { 115 | val percent = mCritical * 100 / Math.max(mTotal, 1) 116 | mPercentTv.setText("暴击率: $percent% $mCritical/$mTotal") 117 | } 118 | 119 | fun random(view: View) { 120 | view.isEnabled = false 121 | val pos = mRandomPicker.next() 122 | 123 | mRadioGroup.check(mRadioGroup.getChildAt(pos).id) 124 | for (index in 0..mWeightLayout.childCount - 1) { 125 | (mWeightLayout.getChildAt(index) as TextView).text = 126 | mRandomPicker.getWeight(index).toString() 127 | } 128 | 129 | mTotal++ 130 | if (pos == mCriticalPos) { 131 | mCritical++ 132 | } 133 | showCriticalTv() 134 | view.isEnabled = true 135 | } 136 | 137 | fun machine(view: View) { 138 | startActivity(Intent(this, GameActivity::class.java)) 139 | } 140 | 141 | fun scoreShuffle(view: View) { 142 | startActivity(Intent(this, ScoreShuffleActivity::class.java)) 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /app/src/main/java/top/wefor/randompicker/game/GameActivity.kt: -------------------------------------------------------------------------------- 1 | package top.wefor.randompicker.game 2 | 3 | import android.os.Bundle 4 | import android.support.v7.app.AppCompatActivity 5 | import top.wefor.randompicker.R 6 | 7 | /** 8 | * TODO Button'set' 可以设置左侧的博弈参数。 9 | * TODO 随机算法至少提供4种:RandomPicker、区间伪随机、独立事件随机、激进随机。了解更多:说明包含的各种算法。 10 | * TODO 庄家收益面板 统计'set'之后的博弈信息。 11 | * TODO 玩家列表 默认填充两个,item需包含这些参数:game按钮、game局数、命中局数、当前营收或亏损、最大营收。 12 | * TODO 底部按钮 添加与移除玩家;自动游戏开关:开启后,每一秒钟每个玩家随机参与或不参与游戏。 13 | * 玩家之间相互独立,每个玩家都对应一个随机生成器。 14 | * 15 | * game博弈 16 | * 17 | * Created on 2018/8/8. 18 | * @author ice 19 | * @GitHub https://github.com/XunMengWinter 20 | */ 21 | class GameActivity : AppCompatActivity() { 22 | 23 | override fun onCreate(savedInstanceState: Bundle?) { 24 | super.onCreate(savedInstanceState) 25 | setContentView(R.layout.activity_game) 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/top/wefor/randompicker/game/ScoreShuffleActivity.kt: -------------------------------------------------------------------------------- 1 | package top.wefor.randompicker.game 2 | 3 | import android.os.Bundle 4 | import android.support.v7.app.AppCompatActivity 5 | import android.view.View 6 | import android.widget.GridLayout 7 | import android.widget.ImageView 8 | import android.widget.TextView 9 | import top.wefor.randompicker.R 10 | import top.wefor.randompicker.RandomPicker 11 | 12 | class ScoreShuffleActivity : AppCompatActivity() { 13 | 14 | private lateinit var mGridLayout: GridLayout 15 | private lateinit var mPercentTv: TextView 16 | private lateinit var mSilenceView: View 17 | 18 | private val mRandomPicker = RandomPicker() 19 | 20 | private val mCriticalPos = 0 21 | private var mTotal = 0 22 | private var mCritical = 0 23 | 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | super.onCreate(savedInstanceState) 26 | setContentView(R.layout.activity_score_shuffle) 27 | 28 | mGridLayout = findViewById(R.id.gridLayout) 29 | mPercentTv = findViewById(R.id.percent_tv) 30 | mSilenceView = findViewById(R.id.silence_view) 31 | 32 | resetRandomPicker() 33 | 34 | mGridLayout.post { 35 | for (index in 0..mGridLayout.childCount - 1) { 36 | val imageView: ImageView = mGridLayout.getChildAt(index) as ImageView 37 | imageView.setOnClickListener { 38 | imageView.isEnabled = false 39 | val randomIndex = mRandomPicker.next() 40 | if (randomIndex == mCriticalPos) { 41 | //命中,重新洗牌 42 | mSilenceView.visibility = View.VISIBLE 43 | imageView.setImageResource(R.drawable.ic_critical) 44 | mCritical++ 45 | mGridLayout.postDelayed({ 46 | resetRandomPicker() 47 | resetGridLayout() 48 | mSilenceView.visibility = View.GONE 49 | }, 1_000) 50 | } else { 51 | imageView.setImageResource(R.drawable.ic_attack) 52 | } 53 | mTotal++ 54 | showCriticalTv() 55 | } 56 | } 57 | } 58 | } 59 | 60 | fun showCriticalTv() { 61 | val percent = mCritical * 100 / Math.max(mTotal, 1) 62 | mPercentTv.setText("暴击率: $percent% $mCritical/$mTotal") 63 | } 64 | 65 | fun resetRandomPicker() { 66 | mRandomPicker.exitCutMode() 67 | mRandomPicker.resetList(9, 1) 68 | mRandomPicker.enterCutMode() 69 | } 70 | 71 | fun resetGridLayout() { 72 | for (index in 0..mGridLayout.childCount - 1) { 73 | val imageView: ImageView = mGridLayout.getChildAt(index) as ImageView 74 | imageView.setImageResource(R.drawable.ic_unknown) 75 | imageView.isEnabled = true 76 | } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/top/wefor/randompicker/music/MusicActivity.kt: -------------------------------------------------------------------------------- 1 | package top.wefor.randompicker.music 2 | 3 | import android.Manifest 4 | import android.media.MediaPlayer 5 | import android.net.Uri 6 | import android.os.Build 7 | import android.os.Bundle 8 | import android.os.CountDownTimer 9 | import android.support.v7.app.AppCompatActivity 10 | import android.support.v7.widget.AppCompatSeekBar 11 | import android.util.Log 12 | import android.widget.CheckBox 13 | import android.widget.CompoundButton 14 | import android.widget.ImageView 15 | import android.widget.SeekBar 16 | import top.wefor.randompicker.R 17 | import top.wefor.randompicker.RandomPicker 18 | import top.wefor.randompicker.showTips 19 | import java.io.File 20 | 21 | /** 22 | * Created on 2018/7/26. 23 | * @author ice 24 | * @GitHub https://github.com/XunMengWinter 25 | */ 26 | class MusicActivity : AppCompatActivity() { 27 | 28 | var mCutMode = false 29 | val mMusicPlayer: MusicPlayer = MusicPlayer(getLifecycle()) 30 | lateinit var mMusicProvider: MusicProvider 31 | lateinit var mRandomPicker: RandomPicker 32 | var mCountDownTimer: CountDownTimer? = null 33 | val mHistoryList = arrayListOf() 34 | var mHistoryIndex = -1 35 | 36 | lateinit var mCutModeCheckBox: CheckBox 37 | lateinit var mNextBtnIv: ImageView 38 | lateinit var mPreviousBtnIv: ImageView 39 | lateinit var mCoverImgIv: ImageView 40 | lateinit var mCoverBgIv: ImageView 41 | lateinit var mProgressSeekBar: AppCompatSeekBar 42 | 43 | override fun onCreate(savedInstanceState: Bundle?) { 44 | super.onCreate(savedInstanceState) 45 | initView() 46 | 47 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 48 | requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 11) 49 | } 50 | } 51 | 52 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { 53 | super.onRequestPermissionsResult(requestCode, permissions, grantResults) 54 | if (requestCode == 11) { 55 | initData() 56 | } 57 | } 58 | 59 | override fun onDestroy() { 60 | mCountDownTimer?.cancel() 61 | super.onDestroy() 62 | } 63 | 64 | fun initView() { 65 | setContentView(R.layout.activity_music) 66 | mCutModeCheckBox = findViewById(R.id.cutMode_checkBox) 67 | mNextBtnIv = findViewById(R.id.nextBtn_iv) 68 | mPreviousBtnIv = findViewById(R.id.previousBtn_iv) 69 | mCoverImgIv = findViewById(R.id.cover_img_iv) 70 | mCoverBgIv = findViewById(R.id.cover_bg_iv) 71 | mProgressSeekBar = findViewById(R.id.progress_seekBar) 72 | } 73 | 74 | fun initData() { 75 | mMusicProvider = MusicProvider(this) 76 | mRandomPicker = RandomPicker(mMusicProvider.mMusicList.size, 1) 77 | 78 | mMusicPlayer.setCompletionListener(object : MediaPlayer.OnCompletionListener { 79 | override fun onCompletion(mediaPlayer: MediaPlayer?) { 80 | next() 81 | } 82 | }) 83 | 84 | mCutModeCheckBox.setOnCheckedChangeListener(object : CompoundButton.OnCheckedChangeListener { 85 | override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { 86 | showTips(buttonView, (if (isChecked) "Enter" else "Exit") + " Cut mode") 87 | 88 | mCutMode = isChecked 89 | if (mCutMode) 90 | mRandomPicker.enterCutMode() 91 | else 92 | mRandomPicker.exitCutMode() 93 | } 94 | }) 95 | 96 | mProgressSeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { 97 | override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) { 98 | } 99 | 100 | override fun onStartTrackingTouch(seekBar: SeekBar?) { 101 | } 102 | 103 | override fun onStopTrackingTouch(seekBar: SeekBar?) { 104 | mMusicPlayer.seekTo((seekBar?.progress ?: 0) / 100F) 105 | } 106 | }) 107 | mNextBtnIv.setOnClickListener { next() } 108 | mPreviousBtnIv.setOnClickListener { previous() } 109 | 110 | mCountDownTimer = object : CountDownTimer(Long.MAX_VALUE, 1_000) { 111 | override fun onTick(millisUntilFinished: Long) { 112 | mProgressSeekBar.setProgress((mMusicPlayer.getProgress() * 100).toInt()) 113 | } 114 | 115 | override fun onFinish() { 116 | 117 | } 118 | } 119 | mCountDownTimer?.start() 120 | } 121 | 122 | fun next() { 123 | if (mMusicProvider.mMusicList.isEmpty()) { 124 | return 125 | } 126 | 127 | if (mHistoryIndex >= 0 && mHistoryIndex < mHistoryList.size - 1) { 128 | mHistoryIndex++ 129 | play(mHistoryList.get(mHistoryIndex)) // play history next 130 | return 131 | } 132 | 133 | val startNanoTime = System.nanoTime() 134 | val index = mRandomPicker.next() 135 | val costNanoTime = System.nanoTime() - startNanoTime 136 | Log.i("xyz ", "RandomPicker cost ${costNanoTime / 1_000_000F} millisecond, cutMode: ${mCutMode}, list size: ${mRandomPicker.size}") 137 | 138 | val musicBean = mMusicProvider.mMusicList.get(index) 139 | play(musicBean) 140 | mHistoryList.add(musicBean) 141 | mHistoryIndex = -1 //reset history index 142 | } 143 | 144 | fun previous() { 145 | if (mHistoryList.isEmpty()) { 146 | next() 147 | return 148 | } 149 | 150 | if (mHistoryIndex < 0) { 151 | mHistoryIndex = mHistoryList.size - 1 // reset index 152 | } 153 | 154 | if (mHistoryIndex > 0) { 155 | mHistoryIndex-- //play history 156 | } 157 | play(mHistoryList.get(mHistoryIndex)) 158 | } 159 | 160 | fun play(musicBean: MusicBean) { 161 | setTitle(musicBean.title) 162 | showImage(musicBean.imageFilePath) 163 | mMusicPlayer.next(musicBean.filePath) 164 | } 165 | 166 | private fun showImage(filePath: String?) { 167 | if (!filePath.isNullOrEmpty()) { 168 | val file = File(filePath) 169 | if (file.exists()) { 170 | mCoverImgIv.setImageURI(Uri.fromFile(file)); 171 | return 172 | } 173 | } 174 | mCoverImgIv.setImageResource(R.drawable.ic_music_note_white_36dp) 175 | } 176 | } -------------------------------------------------------------------------------- /app/src/main/java/top/wefor/randompicker/music/MusicBean.kt: -------------------------------------------------------------------------------- 1 | package top.wefor.randompicker.music 2 | 3 | /** 4 | * Created on 2018/7/26. 5 | * @author ice 6 | * @GitHub https://github.com/XunMengWinter 7 | */ 8 | data class MusicBean(var id: Int = 0, var title: String = "", var artist: String = "" 9 | , var filePath: String = "", var length: Int = 0, internal var imageFilePath: String = "") { 10 | 11 | override fun toString(): String { 12 | return "[Music(title = $title, artist = $artist)]" 13 | } 14 | } -------------------------------------------------------------------------------- /app/src/main/java/top/wefor/randompicker/music/MusicPlayer.kt: -------------------------------------------------------------------------------- 1 | package top.wefor.randompicker.music 2 | 3 | import android.arch.lifecycle.Lifecycle 4 | import android.arch.lifecycle.LifecycleObserver 5 | import android.arch.lifecycle.OnLifecycleEvent 6 | import android.media.MediaPlayer 7 | import java.io.File 8 | import java.io.FileInputStream 9 | 10 | /** 11 | * Created on 2018/7/26. 12 | * @author ice 13 | * @GitHub https://github.com/XunMengWinter 14 | */ 15 | class MusicPlayer(lifecycle: Lifecycle) : LifecycleObserver { 16 | 17 | private var mMediaPlayer = MediaPlayer() 18 | private var mLifecycle: Lifecycle = lifecycle 19 | 20 | init { 21 | mLifecycle.addObserver(this) 22 | } 23 | 24 | private fun play(filePath: String) { 25 | try { 26 | mMediaPlayer.reset() //重置多媒体 27 | val file = File(filePath) 28 | val fd = FileInputStream(file).getFD() 29 | //指定音频文件地址 30 | mMediaPlayer.setDataSource(fd, 0, file.length()) 31 | //准备播放 32 | mMediaPlayer.prepare() 33 | 34 | mMediaPlayer.start() 35 | } catch (e: Exception) { 36 | e.printStackTrace() 37 | } 38 | } 39 | 40 | fun next(musicFilePath: String) { 41 | play(musicFilePath) 42 | } 43 | 44 | fun setCompletionListener(completionListener: MediaPlayer.OnCompletionListener) { 45 | mMediaPlayer.setOnCompletionListener(completionListener) 46 | } 47 | 48 | fun seekTo(progress: Float) { 49 | try { 50 | mMediaPlayer.seekTo((progress * mMediaPlayer.duration).toInt()) 51 | } catch (e: Exception) { 52 | } 53 | } 54 | 55 | fun getProgress(): Float { 56 | return try { 57 | mMediaPlayer.currentPosition.toFloat() / mMediaPlayer.duration 58 | } catch (e: Exception) { 59 | 0f 60 | } 61 | } 62 | 63 | @OnLifecycleEvent(Lifecycle.Event.ON_START) 64 | private fun restart() { 65 | mMediaPlayer.start() 66 | } 67 | 68 | @OnLifecycleEvent(Lifecycle.Event.ON_STOP) 69 | private fun stop() { 70 | mMediaPlayer.pause() 71 | } 72 | 73 | @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) 74 | private fun release() { 75 | mMediaPlayer.stop() 76 | mMediaPlayer.release() 77 | mLifecycle.removeObserver(this) 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /app/src/main/java/top/wefor/randompicker/music/MusicProvider.kt: -------------------------------------------------------------------------------- 1 | package top.wefor.randompicker.music 2 | 3 | import android.content.Context 4 | import android.database.Cursor 5 | import android.os.Environment 6 | import android.provider.MediaStore 7 | import java.io.File 8 | 9 | /** 10 | * Created on 2018/7/26. 11 | * @author ice 12 | * @GitHub https://github.com/XunMengWinter 13 | */ 14 | class MusicProvider(context: Context) { 15 | 16 | val mMusicList = getMusicList(context, Environment.getExternalStorageDirectory()) 17 | 18 | /** 19 | * 极速遍历本地音乐 20 | */ 21 | private fun getMusicList(context: Context, rootDir: File): ArrayList { 22 | val list: ArrayList = arrayListOf() 23 | val cursor = context.getContentResolver().query( 24 | MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, 25 | MediaStore.Audio.Media.DATA + " like ?", 26 | arrayOf(rootDir.getPath() + "%"), 27 | MediaStore.Audio.Media.DEFAULT_SORT_ORDER); 28 | 29 | if (cursor == null) return list; 30 | cursor.moveToFirst() 31 | while (cursor.moveToNext()) { 32 | // 如果不是音乐 33 | val isMusic = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.IS_MUSIC)) 34 | if (isMusic != null && isMusic == "") continue 35 | 36 | val title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE)) 37 | val artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)) 38 | 39 | val music = MusicBean() 40 | music.id = (cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID))) 41 | music.title = (title) 42 | music.artist = (artist) 43 | music.filePath = (cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA))) 44 | music.length = (cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION))) 45 | music.imageFilePath = (getAlbumImage(context, cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID)))).orEmpty() 46 | list.add(music); 47 | } 48 | cursor.close() 49 | // id title singer data time image 50 | return list 51 | } 52 | 53 | private fun getAlbumImage(context: Context, albumId: Int): String? { 54 | var path: String? = "" 55 | var cursor: Cursor? = null 56 | try { 57 | cursor = context.getContentResolver().query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, 58 | arrayOf(MediaStore.Audio.Albums._ID, MediaStore.Audio.Albums.ALBUM_ART), 59 | MediaStore.Audio.Albums._ID + "=?", 60 | arrayOf(albumId.toString()), 61 | null) 62 | if (cursor.moveToFirst()) { 63 | // do whatever you need to do 64 | path = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Albums.ALBUM_ART)) 65 | } 66 | } catch (e: Exception) { 67 | } finally { 68 | cursor?.close() 69 | } 70 | return path 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_attack.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XunMengWinter/RandomPicker/7360ea08ffd21bfd1896734fc3b51f65da5e20ab/app/src/main/res/drawable-hdpi/ic_attack.gif -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_critical.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XunMengWinter/RandomPicker/7360ea08ffd21bfd1896734fc3b51f65da5e20ab/app/src/main/res/drawable-hdpi/ic_critical.gif -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_unknown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XunMengWinter/RandomPicker/7360ea08ffd21bfd1896734fc3b51f65da5e20ab/app/src/main/res/drawable-hdpi/ic_unknown.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/img_disco.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/XunMengWinter/RandomPicker/7360ea08ffd21bfd1896734fc3b51f65da5e20ab/app/src/main/res/drawable-xxhdpi/img_disco.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check_red_600_48dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_content_cut_36dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_content_cut_36dp_checked.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_music_note_white_36dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_playlist_add_check_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_playlist_play_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_skip_next_36dp_pressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_skip_next_white_36dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_skip_previous_36dp_pressed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_skip_previous_white_36dp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selector_cut_checkbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selector_next_btn.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/selector_previous_btn.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_dice.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 17 | 18 | 24 | 25 | 36 | 37 | 47 | 48 | 58 | 59 | 69 | 70 | 71 | 81 | 82 | 83 | 84 | 88 | 89 | 96 | 97 | 98 | 104 | 105 | 111 | 112 | 118 | 119 | 125 | 126 | 132 | 133 | 134 | 140 | 141 | 146 | 147 | 154 | 155 | 160 | 161 |