├── app
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ ├── values
│ │ │ ├── strings.xml
│ │ │ ├── dimens.xml
│ │ │ ├── styles.xml
│ │ │ └── colors.xml
│ │ ├── drawable
│ │ │ ├── ic_done.png
│ │ │ ├── ic_pause.png
│ │ │ ├── ic_play.png
│ │ │ ├── ic_delete.png
│ │ │ ├── bg_translucent_white.xml
│ │ │ ├── icon_add_selector.xml
│ │ │ ├── icon_add_error.xml
│ │ │ ├── icon_add.xml
│ │ │ ├── icon_add_activated.xml
│ │ │ ├── icon_add_pressed.xml
│ │ │ ├── ic_folder.xml
│ │ │ ├── ic_text.xml
│ │ │ ├── ic_calendar.xml
│ │ │ ├── ic_location.xml
│ │ │ ├── ic_mic.xml
│ │ │ ├── ic_contacts.xml
│ │ │ ├── ic_phone.xml
│ │ │ └── ic_camera.xml
│ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ ├── mipmap-ldpi
│ │ │ └── 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
│ │ │ └── attrs.xml
│ │ ├── anim
│ │ │ ├── bounce.xml
│ │ │ ├── fade_in_center.xml
│ │ │ └── fade_out_center.xml
│ │ └── layout
│ │ │ ├── activity_playback.xml
│ │ │ ├── item_permission.xml
│ │ │ ├── activity_main.xml
│ │ │ └── alert_permissions.xml
│ │ ├── java
│ │ └── com
│ │ │ └── water
│ │ │ └── example
│ │ │ ├── utils
│ │ │ ├── PermissionUtils.java
│ │ │ ├── Units.java
│ │ │ ├── BounceInterpolator.java
│ │ │ ├── CustomButton.java
│ │ │ └── PermissionsDialogue.java
│ │ │ ├── PlaybackActivity.java
│ │ │ ├── EasyTimer.java
│ │ │ └── MainActivity.java
│ │ └── AndroidManifest.xml
├── proguard-rules.pro
└── build.gradle
├── amraudiorecorder
├── .gitignore
├── src
│ └── main
│ │ ├── res
│ │ └── values
│ │ │ └── strings.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── water
│ │ └── amraudiorecorder
│ │ └── AMRAudioRecorder.java
├── build.gradle
└── proguard-rules.pro
├── settings.gradle
├── Screenshot
├── demo.jpg
└── app_icon.png
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── LICENSE
├── README.md
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/amraudiorecorder/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':amraudiorecorder'
2 |
--------------------------------------------------------------------------------
/Screenshot/demo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waterzhang0423/AMRAudioRecorder/HEAD/Screenshot/demo.jpg
--------------------------------------------------------------------------------
/Screenshot/app_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waterzhang0423/AMRAudioRecorder/HEAD/Screenshot/app_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AMRAudioRecorderExample
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waterzhang0423/AMRAudioRecorder/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/amraudiorecorder/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AMRAudioRecorder
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waterzhang0423/AMRAudioRecorder/HEAD/app/src/main/res/drawable/ic_done.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waterzhang0423/AMRAudioRecorder/HEAD/app/src/main/res/drawable/ic_pause.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waterzhang0423/AMRAudioRecorder/HEAD/app/src/main/res/drawable/ic_play.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waterzhang0423/AMRAudioRecorder/HEAD/app/src/main/res/drawable/ic_delete.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waterzhang0423/AMRAudioRecorder/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waterzhang0423/AMRAudioRecorder/HEAD/app/src/main/res/mipmap-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waterzhang0423/AMRAudioRecorder/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waterzhang0423/AMRAudioRecorder/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waterzhang0423/AMRAudioRecorder/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waterzhang0423/AMRAudioRecorder/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 12:32:12 CDT 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-all.zip
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_translucent_white.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/amraudiorecorder/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_add_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/bounce.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_add_error.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_add.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_add_activated.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon_add_pressed.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_folder.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_calendar.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fade_in_center.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/fade_out_center.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
13 |
14 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_location.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_mic.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_contacts.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_phone.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
--------------------------------------------------------------------------------
/amraudiorecorder/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 28
5 |
6 | defaultConfig {
7 | minSdkVersion 14
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.0"
11 | }
12 | buildTypes {
13 | release {
14 | minifyEnabled false
15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
16 | }
17 | }
18 | }
19 |
20 | dependencies {
21 | implementation fileTree(dir: 'libs', include: ['*.jar'])
22 | testImplementation 'junit:junit:4.12'
23 | implementation 'androidx.appcompat:appcompat:1.0.2'
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_playback.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/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/WaterZhang/Documents/MacAndroid/android-sdk-macosx/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 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | build/*
3 | */app.iml
4 |
5 | # Crashlytics configuations
6 | com_crashlytics_export_strings.xml
7 |
8 | # Local configuration file (sdk path, etc)
9 | local.properties
10 |
11 | # Gradle generated files
12 | .gradle/
13 |
14 | # Signing files
15 | .signing/
16 |
17 | # User-specific configurations
18 | .idea/*
19 | .idea/libraries/
20 | .idea/workspace.xml
21 | .idea/tasks.xml
22 | .idea/.name
23 | .idea/compiler.xml
24 | .idea/copyright/profiles_settings.xml
25 | .idea/encodings.xml
26 | .idea/misc.xml
27 | .idea/modules.xml
28 | .idea/scopes/scope_settings.xml
29 | .idea/vcs.xml
30 | *.iml
31 | captures/*
32 |
33 | # OS-specific files
34 | .DS_Store
35 | .DS_Store?
36 | ._*
37 | .Spotlight-V100
38 | .Trashes
39 | ehthumbs.db
40 | Thumbs.db
--------------------------------------------------------------------------------
/amraudiorecorder/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/WaterZhang/Documents/MacAndroid/android-sdk-macosx/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/main/res/drawable/ic_camera.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
11 |
16 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 |
6 | defaultConfig {
7 | applicationId "com.water.example"
8 | minSdkVersion 14
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(dir: 'libs', include: ['*.jar'])
23 | testImplementation 'junit:junit:4.12'
24 | implementation 'androidx.appcompat:appcompat:1.0.2'
25 | implementation 'com.google.android.material:material:1.1.0-alpha01'
26 | implementation project(':amraudiorecorder')
27 | }
28 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 | android.enableJetifier=true
20 | android.useAndroidX=true
--------------------------------------------------------------------------------
/app/src/main/java/com/water/example/utils/PermissionUtils.java:
--------------------------------------------------------------------------------
1 | package com.water.example.utils;
2 |
3 | import android.content.Context;
4 | import android.content.pm.PackageManager;
5 | import android.os.Build;
6 |
7 | public class PermissionUtils {
8 |
9 | public static boolean IsPermissionEnabled(Context context, String permission)
10 | {
11 | if (Build.VERSION.SDK_INT >= 23) {
12 | if (context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) {
13 | return true;
14 | }
15 | else
16 | {
17 | return false;
18 | }
19 | }
20 | else
21 | {
22 | return true;
23 | }
24 | }
25 |
26 | public static boolean IsPermissionsEnabled(Context context, String[] permissionsList)
27 | {
28 | for (String permission : permissionsList)
29 | {
30 | if (!IsPermissionEnabled(context, permission))
31 | {
32 | return false;
33 | }
34 | }
35 |
36 | return true;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Water
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AMRAudioRecorder
2 | 
3 |
4 | Android does not support pause and resume when recording amr audio, so we should do a little trick to support pause and resume funciton.
5 | ## Screenshot
6 | 
7 | ## Features
8 | * You can pause recording and resume it
9 |
10 | ## Usage
11 | In Android Studio, just import module `amraudiorecorder`. In other IDE, you should copy `AMRAudioRecorder.java` into your project.
12 |
13 | ~~~java
14 | // Note: this is not the audio file name, it's a directory.
15 | // AMRAudioRecorder will store audio files into this directory.
16 | // And this should be exist,
17 | // AMRAudioRecorder will not make dir if the dir does not exist.
18 | String recordingDirectory = "A directory absolute path";
19 | AMRAudioRecorder mRecorder = new AMRAudioRecorder(recordingDirectory);
20 | mRecorder.start();
21 | ~~~
22 | ## Pause recording
23 | ~~~java
24 | mRecorder.pause();
25 | ~~~
26 | ## Resume recording
27 | ~~~java
28 | mRecorder.resume();
29 | ~~~
30 | ## Stop recording
31 | ~~~java
32 | mRecorder.stop();
33 | ~~~
34 | ## Get recording file path
35 | ~~~java
36 | mRecorder.getAudioFilePath();
37 | ~~~
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
7 | 1dp
8 | 20dp
9 | 10dp
10 | 30dp
11 | 50dp
12 | 30dp
13 | 44dp
14 | 16sp
15 | 10dp
16 | 18dp
17 | 14dp
18 | 12dp
19 | 4dp
20 | 20sp
21 | 14sp
22 | 6dp
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/java/com/water/example/PlaybackActivity.java:
--------------------------------------------------------------------------------
1 | package com.water.example;
2 |
3 | import android.media.MediaPlayer;
4 | import android.os.Bundle;
5 | import androidx.appcompat.app.AppCompatActivity;
6 | import android.view.View;
7 |
8 | public class PlaybackActivity extends AppCompatActivity {
9 |
10 | private MediaPlayer mPlayer;
11 |
12 | @Override
13 | protected void onCreate(Bundle savedInstanceState) {
14 | super.onCreate(savedInstanceState);
15 | setContentView(R.layout.activity_playback);
16 |
17 | String audioFilePath = getIntent().getStringExtra("audioFilePath");
18 | mPlayer = new MediaPlayer();
19 |
20 | try {
21 | mPlayer.setDataSource(audioFilePath);
22 | mPlayer.prepare();
23 | mPlayer.start();
24 | } catch (Exception e) {
25 | e.printStackTrace();
26 | }
27 |
28 | }
29 |
30 | @Override
31 | protected void onDestroy() {
32 | stopPlayback(null);
33 | super.onDestroy();
34 | }
35 |
36 | public void stopPlayback(View view) {
37 | if (mPlayer != null) {
38 | mPlayer.stop();
39 | mPlayer.release();
40 | mPlayer = null;
41 | }
42 |
43 | finish();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/water/example/utils/Units.java:
--------------------------------------------------------------------------------
1 | package com.water.example.utils;
2 |
3 | import android.content.Context;
4 | import android.util.DisplayMetrics;
5 |
6 | public class
7 | Units {
8 |
9 | /**
10 | * Converts dp to pixels.
11 | */
12 | public static int dpToPx(Context context, int dp) {
13 | DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
14 | int px = Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
15 | return px;
16 | }
17 |
18 | /**
19 | * Converts pixels to dp.
20 | */
21 | public static int pxToDp(Context context, int px) {
22 | DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
23 | int dp = Math.round(px / (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT));
24 | return dp;
25 | }
26 |
27 | public static float spToPx(Context context, float sp) {
28 | float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
29 | return sp * scaledDensity;
30 | }
31 |
32 | public static float pxToSp(Context context, float px) {
33 | float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
34 | return px / scaledDensity;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
21 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/water/example/EasyTimer.java:
--------------------------------------------------------------------------------
1 | package com.water.example;
2 |
3 | import android.os.Handler;
4 |
5 | /**
6 | * A convenient way to use timer.
7 | *
8 | * Created by Water Zhang on 12/9/13.
9 | */
10 | public class EasyTimer {
11 | private Handler handler;
12 | private Runnable runnable;
13 | private long delayInterval;
14 | private CallBack callBack;
15 | private boolean stopped;
16 |
17 | public interface CallBack {
18 | void execute();
19 | }
20 |
21 | public EasyTimer (final long millionSeconds,final CallBack callBack) {
22 | this.delayInterval = millionSeconds;
23 | this.callBack = callBack;
24 | start();
25 | }
26 |
27 | public void stop () {
28 | stopped = true;
29 | handler.removeCallbacksAndMessages(null);
30 | }
31 |
32 | public void restart () {
33 | stop();
34 | start();
35 | }
36 |
37 | private void start () {
38 | stopped = false;
39 | handler = new Handler();
40 | runnable = new Runnable() {
41 | @Override
42 | public void run() {
43 | if (!stopped) {
44 | callBack.execute();
45 | handler.postDelayed(runnable,delayInterval);
46 | }
47 | }
48 | };
49 | handler.postDelayed(runnable,delayInterval);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | @android:color/holo_red_light
4 | #ff6666
5 | @android:color/holo_red_dark
6 | #FF4081
7 |
8 |
9 | #2D2D2D
10 | #8C8C8C
11 |
12 |
13 | #bbbbbb
14 | #757575
15 | @android:color/holo_red_light
16 |
17 |
18 | #FFFFFF
19 | #c8ffffff
20 | #000000
21 | #7c000000
22 | #00FFFFFF
23 | #40c4ff
24 | #15CC87
25 | #1affa7
26 | #FE5442
27 | #FCD007
28 | #555555
29 |
30 |
31 | #c8ffffff
32 | #000000
33 |
34 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_permission.xml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
19 |
20 |
32 |
33 |
42 |
43 |
66 |
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/water/example/utils/BounceInterpolator.java:
--------------------------------------------------------------------------------
1 | package com.water.example.utils;
2 |
3 | //
4 | // Interpolator to be used with Android view Animation class to achieve the spring-bounce effect.
5 | //
6 | // License: MIT
7 | // Source: http://evgenii.com/blog/spring-button-animation-on-android/
8 | //
9 | // Usage example, make the button wobble in scale:
10 | // ------------
11 | //
12 | // // Load animation
13 | // final Animation myAnim = AnimationUtils.loadAnimation(this, R.anim.bounce);
14 | // double animationDuration = getDurationValue() * 1000;
15 | //
16 | // // Create interpolator with the amplitude 0.2 and frequency 20
17 | // MyBounceInterpolator interpolator = new MyBounceInterpolator(0.2, 20);
18 | //
19 | // myAnim.setInterpolator(interpolator);
20 | // Button button = (Button)findViewById(R.id.button_to_animate);
21 | // button.startAnimation(myAnim);
22 | //
23 | // anim/bounce.xml file:
24 | // --------------------
25 | //
26 | //
27 | //
28 | //
29 | //
37 | //
38 | //
39 | //
40 | public class BounceInterpolator implements android.view.animation.Interpolator {
41 | /**
42 | * The amplitude of the bounces. The higher value (10, for example) produces more pronounced bounces.
43 | * The lower values (0.1, for example) produce less noticeable wobbles.
44 | */
45 | double mAmplitude = 1;
46 |
47 | /**
48 | * The frequency of the bounces. The higher value produces more wobbles during the animation time period.
49 | */
50 | double mFrequency = 10;
51 |
52 | /**
53 | * Initialize a new interpolator.
54 | *
55 | * @param amplitude The amplitude of the bounces. The higher value produces more pronounced bounces. The lower values (0.1, for example) produce less noticeable wobbles.
56 | * @param frequency The frequency of the bounces. The higher value produces more wobbles during the animation time period.
57 | *
58 | */
59 | public BounceInterpolator(double amplitude, double frequency) {
60 | mAmplitude = amplitude;
61 | mFrequency = frequency;
62 | }
63 |
64 | public float getInterpolation(float time) {
65 | double amplitude = mAmplitude;
66 | if (amplitude == 0) { amplitude = 0.05; }
67 |
68 | // The interpolation curve equation:
69 | // -e^(-time / amplitude) * cos(frequency * time) + 1
70 | //
71 | // View the graph live: https://www.desmos.com/calculator/6gbvrm5i0s
72 | return (float) (-1 * Math.pow(Math.E, -time/ mAmplitude) * Math.cos(mFrequency * time) + 1);
73 | }
74 | }
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
23 |
24 |
37 |
38 |
39 |
51 |
52 |
66 |
67 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/alert_permissions.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
19 |
20 |
31 |
32 |
39 |
40 |
51 |
52 |
60 |
61 |
78 |
79 |
80 |
91 |
92 |
102 |
103 |
111 |
112 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/amraudiorecorder/src/main/java/com/water/amraudiorecorder/AMRAudioRecorder.java:
--------------------------------------------------------------------------------
1 | package com.water.amraudiorecorder;
2 |
3 | import android.media.MediaRecorder;
4 |
5 | import java.io.File;
6 | import java.io.FileInputStream;
7 | import java.io.FileNotFoundException;
8 | import java.io.FileOutputStream;
9 | import java.io.IOException;
10 | import java.util.ArrayList;
11 | import java.util.Date;
12 |
13 | /**
14 | *
15 | * Android does not support pause and resume when recording amr audio,
16 | * so we implement it to provide pause and resume funciton.
17 | *
18 | * Created by Water Zhang on 11/25/15.
19 | */
20 | public class AMRAudioRecorder {
21 |
22 | private boolean singleFile = true;
23 |
24 | private MediaRecorder recorder;
25 |
26 | private ArrayList files = new ArrayList();
27 |
28 | private String fileDirectory;
29 |
30 | private String finalAudioPath;
31 |
32 | private boolean isRecording;
33 |
34 | public boolean isRecording() {
35 | return isRecording;
36 | }
37 |
38 | public String getAudioFilePath() {
39 | return finalAudioPath;
40 | }
41 |
42 | public AMRAudioRecorder (String audioFileDirectory) {
43 | this.fileDirectory = audioFileDirectory;
44 |
45 | if (!this.fileDirectory.endsWith("/")) {
46 | this.fileDirectory += "/";
47 | }
48 |
49 | newRecorder();
50 | }
51 |
52 | public boolean start() {
53 | prepareRecorder();
54 |
55 | try {
56 | recorder.prepare();
57 | } catch (IOException e) {
58 | e.printStackTrace();
59 | return false;
60 | }
61 |
62 | if (recorder != null)
63 | {
64 | recorder.start();
65 | isRecording = true;
66 |
67 | return true;
68 | }
69 |
70 | return false;
71 | }
72 |
73 | public boolean pause() {
74 | if (recorder == null || !isRecording) {
75 | throw new IllegalStateException("[AMRAudioRecorder] recorder is not recording!");
76 | }
77 |
78 | recorder.stop();
79 | recorder.release();
80 | recorder = null;
81 |
82 | isRecording = false;
83 |
84 | return true;
85 | }
86 |
87 | public boolean resume() {
88 | if (isRecording) {
89 | throw new IllegalStateException("[AMRAudioRecorder] recorder is recording!");
90 | }
91 |
92 | singleFile = false;
93 | newRecorder();
94 | return start();
95 | }
96 |
97 | public boolean stop() {
98 | if (!isRecording) {
99 | return merge();
100 | }
101 |
102 | if (recorder == null) {
103 | return false;
104 | }
105 |
106 | recorder.stop();
107 | recorder.release();
108 | recorder = null;
109 | isRecording = false;
110 |
111 | return merge();
112 | }
113 |
114 | public void clear()
115 | {
116 | if (recorder != null || isRecording) {
117 | recorder.stop();
118 | recorder.release();
119 | recorder = null;
120 | isRecording = false;
121 | }
122 | for (int i = 0,len = files.size();i 0) {
148 | for (int j=0; j<6; j++) {
149 | fis.read();
150 | }
151 | }
152 |
153 | byte[] buffer = new byte[512];
154 | int count = 0;
155 | while ( (count = fis.read(buffer)) != -1 ) {
156 | fos.write(buffer,0,count);
157 | }
158 |
159 | fis.close();
160 | fos.flush();
161 | file.delete();
162 | }
163 |
164 | fos.flush();
165 | fos.close();
166 |
167 | this.finalAudioPath = mergedFilePath;
168 | return true;
169 | }
170 | catch (IOException e) {
171 | e.printStackTrace();
172 | }
173 |
174 | return false;
175 | }
176 |
177 | private void newRecorder() {
178 | recorder = new MediaRecorder();
179 | }
180 |
181 | private void prepareRecorder() {
182 | File directory = new File(this.fileDirectory);
183 | if (!directory.exists() || !directory.isDirectory()) {
184 | throw new IllegalArgumentException("[AMRAudioRecorder] audioFileDirectory is a not valid directory!");
185 | }
186 |
187 | String filePath = directory.getAbsolutePath() + "/" + new Date().getTime() + ".amr";
188 | this.files.add(filePath);
189 |
190 | recorder.setOutputFile(filePath);
191 | recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
192 | recorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
193 | recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/app/src/main/java/com/water/example/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.water.example;
2 |
3 | import android.Manifest;
4 | import android.app.Dialog;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.os.Build;
8 | import android.os.Bundle;
9 | import android.os.Environment;
10 | import androidx.appcompat.app.AppCompatActivity;
11 | import android.view.View;
12 | import android.widget.ImageButton;
13 | import android.widget.TextView;
14 |
15 | import com.water.amraudiorecorder.AMRAudioRecorder;
16 | import com.water.example.utils.PermissionUtils;
17 | import com.water.example.utils.PermissionsDialogue;
18 |
19 | import java.io.File;
20 |
21 | public class MainActivity extends AppCompatActivity {
22 |
23 | private int mRecordTimeInterval;
24 |
25 | private TextView mRecordingTime;
26 | private AMRAudioRecorder mRecorder;
27 | private EasyTimer mAudioTimeLabelUpdater;
28 | private PermissionsDialogue.Builder alertPermissions;
29 | private Context mContext;
30 |
31 | private String[] permissionsList = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO};
32 |
33 | @Override
34 | protected void onCreate(Bundle savedInstanceState) {
35 | super.onCreate(savedInstanceState);
36 | setContentView(R.layout.activity_main);
37 |
38 | mContext = getApplicationContext();
39 | mRecordingTime = (TextView) findViewById(R.id.recordingTime);
40 | //Request permissions on Marshmallow and above
41 | if (Build.VERSION.SDK_INT >= 23) {
42 | if (!PermissionUtils.IsPermissionsEnabled(mContext, permissionsList))
43 | {
44 | alertPermissions = new PermissionsDialogue.Builder(this)
45 | .setMessage("AMRAudioRecorder records audio and requires the following permissions: ")
46 | .setRequireStorage(PermissionsDialogue.REQUIRED)
47 | .setRequireAudio(PermissionsDialogue.REQUIRED)
48 | .setOnContinueClicked(new PermissionsDialogue.OnContinueClicked() {
49 | @Override
50 | public void OnClick(View view, Dialog dialog) {
51 | dialog.dismiss();
52 | }
53 | })
54 | .setCancelable(false)
55 | .build();
56 | alertPermissions.show();
57 | }
58 | }
59 | }
60 |
61 | public void viewOnClick(View view) {
62 | switch (view.getId()) {
63 |
64 | case R.id.toggleRecord:
65 |
66 | if (mRecorder == null) {
67 |
68 | resetRecording();
69 |
70 | String sdcardPath = Environment.getExternalStorageDirectory().getAbsolutePath();
71 | String recordingDirectory = sdcardPath + "/wtrecorder/";
72 | File dir = new File(recordingDirectory);
73 | if (!dir.exists()) {
74 | dir.mkdirs();
75 | }
76 |
77 | mRecorder = new AMRAudioRecorder(recordingDirectory);
78 | mRecorder.start();
79 |
80 | ((ImageButton) view).setImageResource(R.drawable.ic_pause);
81 |
82 | mAudioTimeLabelUpdater = new EasyTimer(1000, new EasyTimer.CallBack() {
83 | @Override
84 | public void execute() {
85 | int time = mRecordTimeInterval;
86 |
87 | int min = time / 60,sec = time % 60;
88 |
89 | String minStr = min < 10 ? "0"+min : ""+min;
90 | String secStr = sec < 10 ? "0"+sec : ""+sec;
91 |
92 | mRecordingTime.setText(minStr + ":"+secStr);
93 |
94 | mRecordTimeInterval ++;
95 | }
96 | });
97 | }
98 | else {
99 | if (mRecorder.isRecording()) {
100 | ((ImageButton) view).setImageResource(R.drawable.ic_play);
101 |
102 | mAudioTimeLabelUpdater.stop();
103 | mRecorder.pause();
104 | }
105 | else {
106 | ((ImageButton) view).setImageResource(R.drawable.ic_pause);
107 |
108 | mRecorder.resume();
109 | mAudioTimeLabelUpdater.restart();
110 | }
111 | }
112 |
113 | break;
114 |
115 | case R.id.done:
116 |
117 | if (mRecorder == null) {
118 | return;
119 | }
120 |
121 | mRecorder.stop();
122 |
123 | Intent intent = new Intent(this, PlaybackActivity.class);
124 | intent.putExtra("audioFilePath", mRecorder.getAudioFilePath());
125 | startActivity(intent);
126 |
127 | mRecorder = null;
128 | resetRecording();
129 |
130 | break;
131 |
132 | case R.id.trash:
133 |
134 | if (mRecorder == null) {
135 | return;
136 | }
137 |
138 | mRecorder.clear();
139 | mRecorder = null;
140 | resetRecording();
141 |
142 | break;
143 | }
144 | }
145 |
146 | private void resetRecording () {
147 | if (mAudioTimeLabelUpdater != null) {
148 | mAudioTimeLabelUpdater.stop();
149 | mAudioTimeLabelUpdater = null;
150 | }
151 |
152 | mRecordTimeInterval = 0;
153 | mRecordingTime.setText("00:00");
154 |
155 | ((ImageButton)findViewById(R.id.toggleRecord)).setImageResource(R.drawable.ic_play);
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/app/src/main/java/com/water/example/utils/CustomButton.java:
--------------------------------------------------------------------------------
1 | package com.water.example.utils;
2 |
3 | import android.annotation.TargetApi;
4 | import android.content.Context;
5 | import android.content.res.Resources;
6 | import android.content.res.TypedArray;
7 | import android.graphics.Color;
8 | import android.graphics.drawable.Drawable;
9 | import android.graphics.drawable.GradientDrawable;
10 | import android.os.Build;
11 | import android.text.Spannable;
12 | import android.text.SpannableString;
13 | import android.text.style.ImageSpan;
14 | import android.util.AttributeSet;
15 | import android.view.Gravity;
16 | import android.view.MotionEvent;
17 | import android.view.View;
18 |
19 | import com.water.example.R;
20 |
21 | public class CustomButton extends androidx.appcompat.widget.AppCompatTextView {
22 | private GradientDrawable gradientDrawable;
23 | private int mUnPressColor;
24 | private int mPressColor;
25 | private int mStrokeColor;
26 | private int mStrokeWidth = 2;
27 | private int mCornerRadius = 12;
28 | private Resources resources;
29 | private int defaultStrokeWidth = 2;
30 | private int defaultCornerRadius = 12;
31 | private int textUnPressColor;
32 | private int textPressColor;
33 | private int showDrawable;
34 | private Drawable drawable;
35 |
36 |
37 | public CustomButton(Context context) {
38 | this(context,null,0,0);
39 | }
40 |
41 | public CustomButton(Context context, AttributeSet attrs) {
42 | this(context,attrs, 0,0);
43 | }
44 |
45 | public CustomButton(Context context, AttributeSet attrs, int defStyleAttr) {
46 | this(context, attrs, defStyleAttr, 0);
47 | }
48 |
49 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
50 | public CustomButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
51 | super(context, attrs, defStyleAttr);
52 | init(context, attrs,defStyleAttr,defStyleRes);
53 | }
54 |
55 | private void init(Context context, AttributeSet attrs,int defStyleAttr, int defStyleRes) {
56 | resources = getResources();
57 | if (gradientDrawable == null) {
58 | gradientDrawable = new GradientDrawable();
59 | }
60 |
61 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomButton, defStyleAttr, defStyleRes);
62 | textUnPressColor = typedArray.getColor(R.styleable.CustomButton_btn_text_unpressColor, Color.GRAY);
63 | textPressColor = typedArray.getColor(R.styleable.CustomButton_btn_text_pressColor, Color.WHITE);
64 | mUnPressColor = typedArray.getColor(R.styleable.CustomButton_btn_unpressColor, Color.WHITE);
65 | mPressColor = typedArray.getColor(R.styleable.CustomButton_btn_pressColor, Color.GRAY);
66 | mStrokeColor = typedArray.getColor(R.styleable.CustomButton_btn_strokeColor, Color.GRAY);
67 | mStrokeWidth = typedArray.getDimensionPixelSize(R.styleable.CustomButton_btn_strokeWidth, defaultStrokeWidth);
68 | mCornerRadius = typedArray.getDimensionPixelSize(R.styleable.CustomButton_btn_cornerRadius, defaultCornerRadius);
69 | gradientDrawable.setShape(GradientDrawable.RECTANGLE);
70 | gradientDrawable.setColor(mUnPressColor);
71 | gradientDrawable.setStroke(mStrokeWidth, mStrokeColor, 0, 0);
72 | gradientDrawable.setCornerRadius(mCornerRadius);
73 |
74 | setButtonBackgroud();
75 | typedArray.recycle();
76 |
77 | setGravity(Gravity.CENTER);
78 | setTextColor(textUnPressColor);
79 |
80 | setOnTouchListener(new OnButtonTouchListener());
81 | }
82 |
83 | private void setButtonBackgroud() {
84 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
85 | setBackground(gradientDrawable);
86 | else
87 | setBackgroundDrawable(gradientDrawable);
88 | }
89 |
90 | class OnButtonTouchListener implements OnTouchListener {
91 |
92 | @Override
93 | public boolean onTouch(View v, MotionEvent event) {
94 | switch (event.getAction()) {
95 | case MotionEvent.ACTION_DOWN:
96 | setPressStatus(true);
97 | break;
98 | case MotionEvent.ACTION_CANCEL:
99 | case MotionEvent.ACTION_UP:
100 | setPressStatus(false);
101 | break;
102 | }
103 |
104 | return false;
105 | }
106 | }
107 |
108 | public void setPressStatus(boolean isPress) {
109 | if (isPress) {
110 | setTextColor(textPressColor);
111 | gradientDrawable.setColor(mPressColor);
112 | } else {
113 | setTextColor(textUnPressColor);
114 | gradientDrawable.setColor(mUnPressColor);
115 | }
116 | setButtonBackgroud();
117 | }
118 |
119 |
120 | public void setButtonStatus(boolean isEnable) {
121 | if (isEnable) {
122 | setTextColor(textPressColor);
123 | gradientDrawable.setColor(mPressColor);
124 | } else {
125 | setTextColor(textUnPressColor);
126 | gradientDrawable.setColor(mUnPressColor);
127 | }
128 |
129 | setButtonBackgroud();
130 | }
131 |
132 | public void setDrawableRightText(int text, int imgResId) {
133 | setDrawableRightText(resources.getString(text), imgResId);
134 | }
135 |
136 | public void setDrawableRightText(CharSequence text, int imgResId) {
137 | Drawable drawable = resources.getDrawable(imgResId);
138 | setDrawableRightText(text, drawable);
139 | }
140 |
141 | public void setDrawableRightText(CharSequence text, Drawable drawable) {
142 | setText("");
143 | SpannableString ss = new SpannableString("pics");
144 |
145 | drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
146 | ImageSpan span = new ImageSpan(drawable, ImageSpan.ALIGN_BASELINE);
147 | ss.setSpan(span, 0, ss.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
148 | append(text);
149 | append(" ");
150 | append(ss);
151 | }
152 |
153 | public void setDrawableLeftText(int text, int imgResId) {
154 | setDrawableLeftText(resources.getString(text), imgResId);
155 | }
156 |
157 | public void setDrawableLeftText(CharSequence text, int imgResId) {
158 | Drawable drawable = resources.getDrawable(imgResId);
159 | setDrawableLeftText(text,drawable);
160 | }
161 |
162 | public void setDrawableLeftText(CharSequence text, Drawable drawable){
163 | setText("");
164 | SpannableString ss = new SpannableString("pics");
165 | drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
166 | ImageSpan span = new ImageSpan(drawable, ImageSpan.ALIGN_BASELINE);
167 | ss.setSpan(span, 0, ss.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
168 | append(ss);
169 | append(" ");
170 | append(text);
171 | }
172 |
173 | public void setColor(int textnormal, int textpressed, int buttonunpressed, int buttonpressed, int stroke)
174 | {
175 | textUnPressColor = textnormal;
176 | textPressColor = textpressed;
177 | mUnPressColor = buttonunpressed;
178 | mPressColor = buttonpressed;
179 | mStrokeColor = stroke;
180 | gradientDrawable.setShape(GradientDrawable.RECTANGLE);
181 | gradientDrawable.setColor(mUnPressColor);
182 | gradientDrawable.setStroke(mStrokeWidth, mStrokeColor, 0, 0);
183 | gradientDrawable.setCornerRadius(mCornerRadius);
184 | setButtonBackgroud();
185 | setTextColor(textUnPressColor);
186 | }
187 |
188 | /**
189 | * 颜色加深处理
190 | *
191 | * @param RGBValues RGB的值,由alpha(透明度)、red(红)、green(绿)、blue(蓝)构成,
192 | * Android中我们一般使用它的16进制,
193 | * 例如:"#FFAABBCC",最左边到最右每两个字母就是代表alpha(透明度)、
194 | * red(红)、green(绿)、blue(蓝)。每种颜色值占一个字节(8位),值域0~255
195 | * 所以下面使用移位的方法可以得到每种颜色的值,然后每种颜色值减小一下,在合成RGB颜色,颜色就会看起来深一些了
196 | * @return
197 | */
198 | private int colorBurn(int RGBValues) {
199 | //int alpha = RGBValues >> 24;
200 | int red = RGBValues >> 16 & 0xFF;
201 | int green = RGBValues >> 8 & 0xFF;
202 | int blue = RGBValues & 0xFF;
203 | red = (int) Math.floor(red * (1 - 0.1));
204 | green = (int) Math.floor(green * (1 - 0.1));
205 | blue = (int) Math.floor(blue * (1 - 0.1));
206 | return Color.rgb(red, green, blue);
207 | }
208 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/water/example/utils/PermissionsDialogue.java:
--------------------------------------------------------------------------------
1 | package com.water.example.utils;
2 |
3 | /*
4 | * Created by Ray Li
5 | * © Copyright 2017 Ray LI
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | */
19 |
20 | import android.Manifest;
21 | import android.app.Activity;
22 | import android.app.Dialog;
23 | import android.content.Context;
24 | import android.content.Intent;
25 | import android.content.pm.PackageManager;
26 | import android.graphics.Rect;
27 | import android.net.Uri;
28 | import android.os.Build;
29 | import android.os.Bundle;
30 | import android.os.Handler;
31 | import android.os.Parcel;
32 | import android.os.Parcelable;
33 | import android.provider.Settings;
34 | import androidx.annotation.NonNull;
35 | import androidx.annotation.Nullable;
36 | import androidx.fragment.app.DialogFragment;
37 | import androidx.core.content.ContextCompat;
38 | import androidx.appcompat.app.AppCompatActivity;
39 | import androidx.recyclerview.widget.GridLayoutManager;
40 | import androidx.recyclerview.widget.LinearLayoutManager;
41 | import androidx.recyclerview.widget.RecyclerView;
42 | import android.util.Log;
43 | import android.view.Gravity;
44 | import android.view.LayoutInflater;
45 | import android.view.View;
46 | import android.view.ViewGroup;
47 | import android.view.Window;
48 | import android.view.WindowManager;
49 | import android.view.animation.Animation;
50 | import android.view.animation.AnimationUtils;
51 | import android.widget.Button;
52 | import android.widget.ImageView;
53 | import android.widget.LinearLayout;
54 | import android.widget.TextView;
55 | import android.widget.Toast;
56 |
57 | import com.water.example.R;
58 |
59 | import java.util.ArrayList;
60 |
61 | public class PermissionsDialogue extends DialogFragment {
62 |
63 | public static Integer NOTREQUIRED = 0;
64 | public static Integer REQUIRED = 1;
65 | public static Integer OPTIONAL = 2;
66 | public static String REQUIRE_PHONE = "REQUIRE_PHONE";
67 | public static String REQUIRE_SMS = "REQUIRE_SMS";
68 | public static String REQUIRE_CONTACTS = "REQUIRE_CONTACTS";
69 | public static String REQUIRE_CALENDAR = "REQUIRE_CALENDAR";
70 | public static String REQUIRE_STORAGE = "REQUIRE_STORAGE";
71 | public static String REQUIRE_CAMERA = "REQUIRE_CAMERA";
72 | public static String REQUIRE_AUDIO = "REQUIRE_AUDIO";
73 | public static String REQUIRE_LOCATION = "REQUIRE_LOCATION";
74 | private static final int REQUEST_PERMISSIONS = 1;
75 | private static final int REQUEST_PERMISSION = 2;
76 |
77 | private Button mButton;
78 | private CustomPermissionsAdapter requiredAdapter;
79 | private CustomPermissionsAdapter optionalAdapter;
80 | private ArrayList requiredPermissions;
81 | private ArrayList optionalPermissions;
82 |
83 | private Context mContext;
84 | private Builder builder;
85 | private Integer gravity = Gravity.CENTER;
86 | private static PermissionsDialogue instance = new PermissionsDialogue();
87 | public static final String TAG = PermissionsDialogue.class.getSimpleName();
88 |
89 | public static PermissionsDialogue getInstance() {
90 | return instance;
91 | }
92 |
93 | @Override
94 | public void onCreate(@Nullable Bundle savedInstanceState) {
95 |
96 | setStyle(DialogFragment.STYLE_NO_TITLE, R.style.PermissionsDialogue);
97 | setRetainInstance(true);
98 | super.onCreate(savedInstanceState);
99 | }
100 |
101 | @Override
102 | public void onSaveInstanceState(Bundle outState) {
103 | super.onSaveInstanceState(outState);
104 | if (builder != null)
105 | outState.putParcelable(Builder.class.getSimpleName(), builder);
106 | }
107 |
108 |
109 | @NonNull
110 | @Override
111 | public Dialog onCreateDialog(Bundle savedInstanceState) {
112 | Dialog dialog = super.onCreateDialog(savedInstanceState);
113 | //Configure floating window
114 | Window window = dialog.getWindow();
115 | WindowManager.LayoutParams wlp = window.getAttributes();
116 | wlp.width = WindowManager.LayoutParams.MATCH_PARENT; //Floating window WRAPS_CONTENT by default. Force fullscreen
117 | wlp.height = WindowManager.LayoutParams.MATCH_PARENT;
118 | wlp.windowAnimations = R.style.CustomDialogAnimation;
119 | wlp.gravity = Gravity.CENTER;
120 | window.setAttributes(wlp);
121 |
122 | if (builder != null) {
123 | if (!builder.getCancelable()) {
124 | setCancelable(false);
125 | } else {
126 | setCancelable(true);
127 | }
128 | }
129 |
130 | return dialog;
131 | }
132 |
133 | @Nullable
134 | @Override
135 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
136 | return inflater.inflate(R.layout.alert_permissions, container, false);
137 | }
138 |
139 | @Override
140 | public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
141 | super.onViewCreated(view, savedInstanceState);
142 |
143 | mButton = (Button) getView().findViewById(R.id.permissions_btn);
144 | mContext = getContext();
145 |
146 | initPermissionsView(view);
147 | }
148 |
149 | @Override
150 | public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
151 | Log.d("Request Code", String.valueOf(requestCode));
152 | switch (requestCode) {
153 | case REQUEST_PERMISSIONS: {
154 | Log.d("Permissons", "Request Permissions");
155 | refreshRequiredPermissions();
156 | boolean permissionsGranted = true;
157 | if (grantResults.length > 0) {
158 | for (int i = 0; i < grantResults.length; i++) {
159 | if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
160 | boolean showRationale = shouldShowRequestPermissionRationale(permissions[i]);
161 | if (!showRationale) {
162 | permissionsGranted = false;
163 | }
164 | }
165 | }
166 | }
167 |
168 | if (permissionsGranted) {
169 | refreshPermissionsButton(false);
170 | Log.d("Permissions", "Granted");
171 | } else {
172 | refreshPermissionsButton(true);
173 | Log.d("Permissions", "Denied");
174 | }
175 | return;
176 | }
177 |
178 | case REQUEST_PERMISSION: {
179 | if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
180 | refreshOptionalPermissions();
181 | } else {
182 |
183 | }
184 | return;
185 | }
186 | }
187 | }
188 |
189 | private void initPermissionsView(View view)
190 | {
191 | if (builder != null) {
192 |
193 | Log.d(TAG, "Builder Not Null");
194 |
195 | if (builder.getRequiredPermissions().size() != 0)
196 | {
197 | initPermissionsRecyclerView(view);
198 | initPermissionsButton(view);
199 | }
200 | else
201 | {
202 | LinearLayout permissionsLayout = (LinearLayout) view.findViewById(R.id.permissions_required);
203 | permissionsLayout.setVisibility(View.GONE);
204 | }
205 |
206 | if (builder.getOptionalPermissions().size() != 0)
207 | {
208 | initOptionalPermissionsRecyclerView(view);
209 | }
210 | else
211 | {
212 | LinearLayout permissionsLayout = (LinearLayout) view.findViewById(R.id.permissions_optional);
213 | permissionsLayout.setVisibility(View.GONE);
214 | }
215 | }
216 | else
217 | {
218 | Log.d(TAG, "Builder Null");
219 | }
220 | }
221 |
222 | private void initPermissionsRecyclerView(View view) {
223 | if (builder.getTitle() != null)
224 | {
225 | TextView title = (TextView) view.findViewById(R.id.title);
226 | title.setText(builder.getTitle());
227 | }
228 | if (builder.getMessage() != null)
229 | {
230 | TextView message = (TextView) view.findViewById(R.id.message);
231 | message.setText(builder.getMessage());
232 | }
233 | else
234 | {
235 | TextView message = (TextView) view.findViewById(R.id.message);
236 | message.setVisibility(View.GONE);
237 | }
238 | if (builder.getIcon() == false)
239 | {
240 | ImageView icon = (ImageView) view.findViewById(R.id.icon);
241 | icon.setVisibility(View.GONE);
242 | }
243 |
244 | requiredPermissions = new ArrayList<>();
245 | requiredPermissions = builder.getRequiredPermissions();
246 | int spanSize = requiredPermissions.size();
247 | RecyclerView permissionsRecyclerView = (RecyclerView) view.findViewById(R.id.permissions_list);
248 | requiredAdapter = new CustomPermissionsAdapter(mContext, requiredPermissions, false);
249 | permissionsRecyclerView.setAdapter(requiredAdapter);
250 | GridLayoutManager layoutManager= new GridLayoutManager(mContext, spanSize, LinearLayoutManager.VERTICAL, false);
251 | permissionsRecyclerView.setLayoutManager(layoutManager);
252 | }
253 |
254 | private void initOptionalPermissionsRecyclerView(View view) {
255 | optionalPermissions = new ArrayList<>();
256 | optionalPermissions = builder.getOptionalPermissions();
257 | int spanSize = optionalPermissions.size();
258 | if (spanSize > 2)
259 | {
260 | spanSize = 2;
261 | }
262 | RecyclerView permissionsRecyclerView = (RecyclerView) view.findViewById(R.id.permissions_list_optional);
263 | optionalAdapter = new CustomPermissionsAdapter(mContext, optionalPermissions, true);
264 | permissionsRecyclerView.setAdapter(optionalAdapter);
265 | GridLayoutManager layoutManager= new GridLayoutManager(mContext, spanSize, LinearLayoutManager.VERTICAL, false);
266 | permissionsRecyclerView.setLayoutManager(layoutManager);
267 | int spacing = Units.dpToPx(mContext, 40);
268 | boolean includeEdge = true;
269 | permissionsRecyclerView.addItemDecoration(new GridSpacingItemDecoration(spanSize, spacing, includeEdge));
270 | }
271 |
272 | private void initPermissionsButton(View view) {
273 | mButton = (Button) view.findViewById(R.id.permissions_btn);
274 | if (builder.getRequiredRequestPermissions().size() == 0)
275 | {
276 | mButton.setText("Continue");
277 | mButton.setBackground(ContextCompat.getDrawable(mContext, R.drawable.icon_add_activated));
278 | if (builder.getOnContinueClicked() != null)
279 | {
280 | mButton.setOnClickListener(new View.OnClickListener() {
281 | @Override
282 | public void onClick(View view) {
283 | builder.getOnContinueClicked().OnClick(getView(), getDialog());
284 | }
285 | });
286 | }
287 | else
288 | {
289 | mButton.setOnClickListener(new View.OnClickListener() {
290 | @Override
291 | public void onClick(View view) {
292 | mButton.startAnimation(AnimateButton());
293 | Handler handler = new Handler();
294 | Runnable r = new Runnable() {
295 | public void run() {
296 | dismiss();
297 | }
298 | };
299 | handler.postDelayed(r, 250);
300 | }
301 | });
302 | }
303 | }
304 | else
305 | {
306 | mButton.setOnClickListener(new View.OnClickListener() {
307 | @Override
308 | public void onClick(View view) {
309 | mButton.startAnimation(AnimateButton());
310 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
311 | ArrayList requestPermissions = builder.getRequiredRequestPermissions();
312 | if (!requestPermissions.isEmpty()) {
313 | requestPermissions(requestPermissions.toArray(new String[requestPermissions.size()]), REQUEST_PERMISSIONS);
314 | }
315 | else
316 | {
317 |
318 | }
319 | } else {
320 |
321 | }
322 | }
323 | });
324 | }
325 | }
326 |
327 | private void refreshRequiredPermissions()
328 | {
329 | requiredPermissions = builder.getRequiredPermissions();
330 | requiredAdapter.permissionsList = requiredPermissions;
331 | requiredAdapter.notifyDataSetChanged();
332 | }
333 |
334 | private void refreshOptionalPermissions()
335 | {
336 | optionalPermissions = builder.getOptionalPermissions();
337 | optionalAdapter.permissionsList = optionalPermissions;
338 | optionalAdapter.notifyDataSetChanged();
339 | }
340 |
341 | private void refreshPermissionsButton(boolean denied)
342 | {
343 | if (denied)
344 | {
345 | mButton.setText("DENIED - Open Settings");
346 | mButton.setBackground(ContextCompat.getDrawable(mContext, R.drawable.icon_add_error));
347 | mButton.setOnClickListener(new View.OnClickListener() {
348 | @Override
349 | public void onClick(View view) {
350 | dismiss();
351 | Toast.makeText(mContext, "Click Permissions to enable permissions", Toast.LENGTH_LONG).show();
352 | Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
353 | Uri.parse("package:" + getActivity().getPackageName()));
354 | intent.addCategory(Intent.CATEGORY_DEFAULT);
355 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
356 | startActivity(intent);
357 | }
358 | });
359 | }
360 | else if (builder.getRequiredRequestPermissions().size() == 0)
361 | {
362 | mButton.setText("Success!");
363 | mButton.setBackground(ContextCompat.getDrawable(mContext, R.drawable.icon_add_activated));
364 | if (builder.getOnContinueClicked() != null)
365 | {
366 | mButton.setOnClickListener(new View.OnClickListener() {
367 | @Override
368 | public void onClick(View view) {
369 | builder.getOnContinueClicked().OnClick(getView(), getDialog());
370 | }
371 | });
372 | }
373 | else
374 | {
375 | mButton.setOnClickListener(new View.OnClickListener() {
376 | @Override
377 | public void onClick(View view) {
378 | mButton.startAnimation(AnimateButton());
379 | Handler handler = new Handler();
380 | Runnable r = new Runnable() {
381 | public void run() {
382 | dismiss();
383 | }
384 | };
385 | handler.postDelayed(r, 250);
386 | }
387 | });
388 | }
389 |
390 | Handler handler = new Handler();
391 | Runnable r = new Runnable() {
392 | public void run() {
393 | mButton.setText("Continue");
394 | }
395 | };
396 | handler.postDelayed(r, 1500);
397 | }
398 | else {
399 | mButton.setText("Permission Denied");
400 | mButton.setBackground(ContextCompat.getDrawable(mContext, R.drawable.icon_add_error));
401 | mButton.setOnClickListener(new View.OnClickListener() {
402 | @Override
403 | public void onClick(View view) {
404 | mButton.startAnimation(AnimateButton());
405 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
406 | ArrayList requestPermissions = builder.getRequiredRequestPermissions();
407 | if (!requestPermissions.isEmpty()) {
408 | requestPermissions(requestPermissions.toArray(new String[requestPermissions.size()]), REQUEST_PERMISSIONS);
409 | } else {
410 |
411 | }
412 | } else {
413 |
414 | }
415 | }
416 | });
417 |
418 | Handler handler = new Handler();
419 | Runnable r = new Runnable() {
420 | public void run() {
421 | mButton.setText("Grant Permissions");
422 | mButton.setBackground(ContextCompat.getDrawable(mContext, R.drawable.icon_add));
423 | }
424 | };
425 | handler.postDelayed(r, 1500);
426 | }
427 | mButton.startAnimation(AnimateButton());
428 | }
429 |
430 | private Dialog show(Activity activity, Builder builder) {
431 | this.builder = builder;
432 | if (!isAdded())
433 | show(((AppCompatActivity) activity).getSupportFragmentManager(), TAG);
434 | return getDialog();
435 | }
436 |
437 | public static class Builder implements Parcelable {
438 |
439 | private String title;
440 | private String message;
441 |
442 | private OnContinueClicked onContinueClicked;
443 |
444 | private boolean autoHide;
445 | private boolean cancelable = true;
446 | private boolean showIcon = true;
447 | private Integer phone = 0;
448 | private Integer sms = 0;
449 | private Integer contacts = 0;
450 | private Integer calendar = 0;
451 | private Integer storage = 0;
452 | private Integer camera = 0;
453 | private Integer audio = 0;
454 | private Integer location = 0;
455 | private String phonedescription;
456 | private String smsdescription;
457 | private String contactsdescription;
458 | private String calendardescription;
459 | private String storagedescription;
460 | private String cameradescription;
461 | private String audiodescription;
462 | private String locationdescription;
463 |
464 | private Context context;
465 |
466 | protected Builder(Parcel in) {
467 | title = in.readString();
468 | message = in.readString();
469 | autoHide = in.readByte() != 0;
470 | cancelable = in.readByte() != 0;
471 | phone = in.readInt();
472 | sms = in.readInt();
473 | contacts = in.readInt();
474 | calendar = in.readInt();
475 | storage = in.readInt();
476 | camera = in.readInt();
477 | audio = in.readInt();
478 | location = in.readInt();
479 | }
480 |
481 | public static final Creator CREATOR = new Creator() {
482 | @Override
483 | public Builder createFromParcel(Parcel in) {
484 | return new Builder(in);
485 | }
486 |
487 | @Override
488 | public Builder[] newArray(int size) {
489 | return new Builder[size];
490 | }
491 | };
492 |
493 | public Builder setContext(Context context) {
494 | this.context = context;
495 | return this;
496 | }
497 | public Context getContext() {
498 | return context;
499 | }
500 |
501 | public Builder getBuilder() { return this; }
502 |
503 | public Builder setTitle(String title) {
504 | this.title = title;
505 | return this;
506 | }
507 | public String getTitle() { return title; }
508 |
509 | public Builder setMessage(String message) {
510 | this.message = message;
511 | return this;
512 | }
513 | public String getMessage() {
514 | return message;
515 | }
516 |
517 | public Builder setOnContinueClicked(OnContinueClicked onContinueClicked) {
518 | this.onContinueClicked = onContinueClicked;
519 | return this;
520 | }
521 | public OnContinueClicked getOnContinueClicked() {
522 | return onContinueClicked;
523 | }
524 |
525 | public Builder setAutoHide(boolean autoHide) {
526 | this.autoHide = autoHide;
527 | return this;
528 | }
529 | public boolean isAutoHide() {
530 | return autoHide;
531 | }
532 |
533 | public Builder setCancelable(boolean cancelable) {
534 | this.cancelable = cancelable;
535 | return this;
536 | }
537 | public boolean getCancelable() { return cancelable; }
538 |
539 | public Builder setIcon(boolean showicon) {
540 | this.showIcon = showicon;
541 | return this;
542 | }
543 | public boolean getIcon() { return showIcon; }
544 |
545 | public Builder setActivity(Context context) {
546 | this.context = context;
547 | return this;
548 | }
549 |
550 | public Builder setRequirePhone(Integer phone) {
551 | this.phone = phone;
552 | return this;
553 | }
554 | public Integer requirePhone() {
555 | return phone;
556 | }
557 |
558 | public Builder setRequireSMS(Integer sms) {
559 | this.sms = sms;
560 | return this;
561 | }
562 | public Integer requireSMS() {
563 | return sms;
564 | }
565 |
566 | public Builder setRequireContacts(Integer contacts) {
567 | this.contacts = contacts;
568 | return this;
569 | }
570 | public Integer requireContacts() {
571 | return contacts;
572 | }
573 |
574 | public Builder setRequireCalendar(Integer calendar) {
575 | this.calendar = calendar;
576 | return this;
577 | }
578 | public Integer requireCalendar() {
579 | return calendar;
580 | }
581 |
582 | public Builder setRequireStorage(Integer storage) {
583 | this.storage = storage;
584 | return this;
585 | }
586 | public Integer requireStorage() {
587 | return storage;
588 | }
589 |
590 | public Builder setRequireCamera(Integer camera) {
591 | this.camera = camera;
592 | return this;
593 | }
594 | public Integer requireCamera() {
595 | return camera;
596 | }
597 |
598 | public Builder setRequireAudio(Integer audio) {
599 | this.audio = audio;
600 | return this;
601 | }
602 | public Integer requireAudio() {
603 | return audio;
604 | }
605 |
606 | public Builder setRequireLocation(Integer location) {
607 | this.location = location;
608 | return this;
609 | }
610 | public Integer requireLocation() {
611 | return location;
612 | }
613 |
614 | public Builder setPhoneDescription(String phonedescription) {
615 | this.phonedescription = phonedescription;
616 | return this;
617 | }
618 | public String getPhoneDescription() {
619 | return phonedescription;
620 | }
621 |
622 | public Builder setSMSDescription(String smsdescription) {
623 | this.smsdescription = smsdescription;
624 | return this;
625 | }
626 | public String getSMSDescription() {
627 | return smsdescription;
628 | }
629 |
630 | public Builder setContactDescription(String contactsdescription) {
631 | this.contactsdescription = contactsdescription;
632 | return this;
633 | }
634 | public String getContactDescription() {
635 | return contactsdescription;
636 | }
637 |
638 | public Builder setCalendarDescription(String calendardescription) {
639 | this.calendardescription = calendardescription;
640 | return this;
641 | }
642 | public String getCalendarDescription() {
643 | return calendardescription;
644 | }
645 |
646 | public Builder setStorageDescription(String storagedescription) {
647 | this.storagedescription = storagedescription;
648 | return this;
649 | }
650 | public String getStorageDescription() {
651 | return storagedescription;
652 | }
653 |
654 | public Builder setCameraDescription(String cameradescription) {
655 | this.cameradescription = cameradescription;
656 | return this;
657 | }
658 | public String getCameraDescription() {
659 | return cameradescription;
660 | }
661 |
662 | public Builder setAudioDescription(String audiodescription) {
663 | this.audiodescription = audiodescription;
664 | return this;
665 | }
666 | public String getAudioDescription() {
667 | return audiodescription;
668 | }
669 |
670 | public Builder setLocationDescription(String locationdescription) {
671 | this.locationdescription = locationdescription;
672 | return this;
673 | }
674 | public String getLocationDescription() {
675 | return locationdescription;
676 | }
677 |
678 | public ArrayList getRequiredPermissions()
679 | {
680 | ArrayList requiredPermissions = new ArrayList<>();
681 | if (requirePhone() == REQUIRED)
682 | {
683 | requiredPermissions.add(REQUIRE_PHONE);
684 | }
685 | if (requireSMS() == REQUIRED)
686 | {
687 | requiredPermissions.add(REQUIRE_SMS);
688 | }
689 | if (requireContacts() == REQUIRED)
690 | {
691 | requiredPermissions.add(REQUIRE_CONTACTS);
692 | }
693 | if (requireCalendar() == REQUIRED)
694 | {
695 | requiredPermissions.add(REQUIRE_CALENDAR);
696 | }
697 | if (requireStorage() == REQUIRED)
698 | {
699 | requiredPermissions.add(REQUIRE_STORAGE);
700 | }
701 | if (requireCamera() == REQUIRED)
702 | {
703 | requiredPermissions.add(REQUIRE_CAMERA);
704 | }
705 | if (requireAudio() == REQUIRED)
706 | {
707 | requiredPermissions.add(REQUIRE_AUDIO);
708 | }
709 | if (requireLocation() == REQUIRED)
710 | {
711 | requiredPermissions.add(REQUIRE_LOCATION);
712 | }
713 | return requiredPermissions;
714 | }
715 |
716 | public ArrayList getOptionalPermissions()
717 | {
718 | ArrayList requiredPermissions = new ArrayList<>();
719 | if (requirePhone() == OPTIONAL)
720 | {
721 | requiredPermissions.add(REQUIRE_PHONE);
722 | }
723 | if (requireSMS() == OPTIONAL)
724 | {
725 | requiredPermissions.add(REQUIRE_SMS);
726 | }
727 | if (requireContacts() == OPTIONAL)
728 | {
729 | requiredPermissions.add(REQUIRE_CONTACTS);
730 | }
731 | if (requireCalendar() == OPTIONAL)
732 | {
733 | requiredPermissions.add(REQUIRE_CALENDAR);
734 | }
735 | if (requireStorage() == OPTIONAL)
736 | {
737 | requiredPermissions.add(REQUIRE_STORAGE);
738 | }
739 | if (requireCamera() == OPTIONAL)
740 | {
741 | requiredPermissions.add(REQUIRE_CAMERA);
742 | }
743 | if (requireAudio() == OPTIONAL)
744 | {
745 | requiredPermissions.add(REQUIRE_AUDIO);
746 | }
747 | if (requireLocation() == OPTIONAL)
748 | {
749 | requiredPermissions.add(REQUIRE_LOCATION);
750 | }
751 | return requiredPermissions;
752 | }
753 |
754 | public ArrayList getRequiredRequestPermissions()
755 | {
756 | ArrayList requestPermissions = new ArrayList<>();
757 | if (requirePhone() == REQUIRED) {
758 | if (!PermissionUtils.IsPermissionEnabled(context, Manifest.permission.CALL_PHONE)) {
759 | requestPermissions.add(Manifest.permission.CALL_PHONE);
760 | }
761 | }
762 | if (requireSMS() == REQUIRED) {
763 | if (!PermissionUtils.IsPermissionEnabled(context, Manifest.permission.SEND_SMS)) {
764 | requestPermissions.add(Manifest.permission.SEND_SMS);
765 | }
766 | }
767 | if (requireContacts() == REQUIRED) {
768 | if (!PermissionUtils.IsPermissionEnabled(context, Manifest.permission.WRITE_CONTACTS)) {
769 | requestPermissions.add(Manifest.permission.WRITE_CONTACTS);
770 | }
771 | }
772 | if (requireCalendar() == REQUIRED) {
773 | if (!PermissionUtils.IsPermissionEnabled(context, Manifest.permission.WRITE_CALENDAR)) {
774 | requestPermissions.add(Manifest.permission.WRITE_CALENDAR);
775 | }
776 | }
777 | if (requireStorage() == REQUIRED) {
778 | if (!PermissionUtils.IsPermissionEnabled(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
779 | requestPermissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
780 | }
781 | }
782 | if (requireCamera() == REQUIRED) {
783 | if (!PermissionUtils.IsPermissionEnabled(context, Manifest.permission.CAMERA)) {
784 | requestPermissions.add(Manifest.permission.CAMERA);
785 | }
786 | }
787 | if (requireAudio() == REQUIRED) {
788 | if (!PermissionUtils.IsPermissionEnabled(context, Manifest.permission.RECORD_AUDIO)) {
789 | requestPermissions.add(Manifest.permission.RECORD_AUDIO);
790 | }
791 | }
792 | if (requireLocation() == REQUIRED) {
793 | if (!PermissionUtils.IsPermissionEnabled(context, Manifest.permission.ACCESS_FINE_LOCATION)) {
794 | requestPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
795 | }
796 | }
797 | return requestPermissions;
798 | }
799 |
800 | public ArrayList getAllRequestPermissions()
801 | {
802 | ArrayList requestPermissions = new ArrayList<>();
803 | if (requirePhone() > NOTREQUIRED) {
804 | if (!PermissionUtils.IsPermissionEnabled(context, Manifest.permission.CALL_PHONE)) {
805 | requestPermissions.add(Manifest.permission.CALL_PHONE);
806 | }
807 | }
808 | if (requireSMS() > NOTREQUIRED) {
809 | if (!PermissionUtils.IsPermissionEnabled(context, Manifest.permission.SEND_SMS)) {
810 | requestPermissions.add(Manifest.permission.SEND_SMS);
811 | }
812 | }
813 | if (requireContacts() > NOTREQUIRED) {
814 | if (!PermissionUtils.IsPermissionEnabled(context, Manifest.permission.WRITE_CONTACTS)) {
815 | requestPermissions.add(Manifest.permission.WRITE_CONTACTS);
816 | }
817 | }
818 | if (requireCalendar() > NOTREQUIRED) {
819 | if (!PermissionUtils.IsPermissionEnabled(context, Manifest.permission.WRITE_CALENDAR)) {
820 | requestPermissions.add(Manifest.permission.WRITE_CALENDAR);
821 | }
822 | }
823 | if (requireStorage() > NOTREQUIRED) {
824 | if (!PermissionUtils.IsPermissionEnabled(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
825 | requestPermissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
826 | }
827 | }
828 | if (requireCamera() > NOTREQUIRED) {
829 | if (!PermissionUtils.IsPermissionEnabled(context, Manifest.permission.CAMERA)) {
830 | requestPermissions.add(Manifest.permission.CAMERA);
831 | }
832 | }
833 | if (requireAudio() > NOTREQUIRED) {
834 | if (!PermissionUtils.IsPermissionEnabled(context, Manifest.permission.RECORD_AUDIO)) {
835 | requestPermissions.add(Manifest.permission.RECORD_AUDIO);
836 | }
837 | }
838 | if (requireLocation() > NOTREQUIRED) {
839 | if (!PermissionUtils.IsPermissionEnabled(context, Manifest.permission.ACCESS_FINE_LOCATION)) {
840 | requestPermissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
841 | }
842 | }
843 | return requestPermissions;
844 | }
845 |
846 | public Builder(Context context) { this.context = context; }
847 |
848 | public Builder build() {
849 | return this;
850 | }
851 |
852 | public Dialog show() {
853 | return PermissionsDialogue.getInstance().show(((Activity) context), this);
854 | }
855 |
856 | @Override
857 | public int describeContents() {
858 | return 0;
859 | }
860 |
861 | @Override
862 | public void writeToParcel(Parcel parcel, int i) {
863 | }
864 | }
865 |
866 | @Override
867 | public void onAttach(Context context) {
868 | super.onAttach(context);
869 | }
870 |
871 | public interface OnContinueClicked {
872 | void OnClick(View view, Dialog dialog);
873 | }
874 |
875 | public interface OnNegativeClicked {
876 | void OnClick(View view, Dialog dialog);
877 | }
878 |
879 | public interface OnCancelClicked {
880 | void OnClick(View view, Dialog dialog);
881 | }
882 |
883 | public Animation AnimateButton() {
884 | // Load the animation
885 | final Animation animation = AnimationUtils.loadAnimation(mContext, R.anim.bounce);
886 | double animationDuration = 1250;
887 | animation.setDuration((long) animationDuration);
888 |
889 | // Use custom animation interpolator to achieve the bounce effect
890 | BounceInterpolator interpolator = new BounceInterpolator(0.2, 20);
891 | animation.setInterpolator(interpolator);
892 |
893 | return animation;
894 | }
895 |
896 | public class CustomPermissionsAdapter extends RecyclerView.Adapter {
897 |
898 | public Context mContext;
899 | public ArrayList permissionsList;
900 | public boolean optional;
901 |
902 | public CustomPermissionsAdapter(Context context, ArrayList permissionsList, boolean optional) {
903 | this.mContext = context;
904 | this.permissionsList = permissionsList;
905 | this.optional = optional;
906 | }
907 |
908 | @Override
909 | public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
910 | View itemView;
911 | itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_permission, parent, false);
912 | return new PermissionViewHolder(itemView);
913 | }
914 |
915 | @Override
916 | public void onBindViewHolder(RecyclerView.ViewHolder genericHolder, final int position) {
917 | String permission = permissionsList.get(position);
918 | PermissionViewHolder holder = (PermissionViewHolder) genericHolder;
919 | holder.setPermission(permission, optional);
920 | }
921 |
922 | @Override
923 | public int getItemCount() { return permissionsList.size(); }
924 |
925 | public String getPermission(int position) { return permissionsList.get(position); }
926 | }
927 |
928 | public class PermissionViewHolder extends RecyclerView.ViewHolder {
929 |
930 | public String permission;
931 | public TextView mName;
932 | public TextView mMessage;
933 | public ImageView mImage;
934 | public CustomButton mButton;
935 | public boolean optional;
936 | public Context mContext;
937 |
938 | public PermissionViewHolder(View itemView) {
939 | super(itemView);
940 |
941 | mName = (TextView) itemView.findViewById(R.id.permission_name);
942 | mMessage = (TextView) itemView.findViewById(R.id.permission_detail);
943 | mImage = (ImageView) itemView.findViewById(R.id.permission_icon);
944 | mButton = (CustomButton) itemView.findViewById(R.id.permission_btn);
945 | mContext = itemView.getContext();
946 | }
947 |
948 | public void setPermission(String permission, boolean optional) {
949 | this.permission = permission;
950 | this.optional = optional;
951 |
952 | if (!optional)
953 | mButton.setVisibility(View.GONE);
954 |
955 | if (REQUIRE_PHONE.equals(permission))
956 | {
957 | mName.setText("Phone");
958 | mImage.setImageResource(R.drawable.ic_phone);
959 | setRequestPermissions(Manifest.permission.CALL_PHONE);
960 |
961 | if (builder.getPhoneDescription() != null)
962 | {
963 | mMessage.setText(builder.getPhoneDescription());
964 | mMessage.setVisibility(View.VISIBLE);
965 | }
966 | else
967 | {
968 | mMessage.setVisibility(View.GONE);
969 | }
970 | }
971 | else if (REQUIRE_SMS.equals(permission))
972 | {
973 | mName.setText("SMS");
974 | mImage.setImageResource(R.drawable.ic_text);
975 |
976 | setRequestPermissions(Manifest.permission.SEND_SMS);
977 |
978 | if (builder.getSMSDescription() != null)
979 | {
980 | mMessage.setText(builder.getSMSDescription());
981 | mMessage.setVisibility(View.VISIBLE);
982 | }
983 | else
984 | {
985 | mMessage.setVisibility(View.GONE);
986 | }
987 | }
988 | else if (REQUIRE_CONTACTS.equals(permission))
989 | {
990 | mName.setText("Contacts");
991 |
992 | mImage.setImageResource(R.drawable.ic_contacts);
993 |
994 | setRequestPermissions(Manifest.permission.WRITE_CONTACTS);
995 |
996 | if (builder.getContactDescription() != null)
997 | {
998 | mMessage.setText(builder.getContactDescription());
999 | mMessage.setVisibility(View.VISIBLE);
1000 | }
1001 | else
1002 | {
1003 | mMessage.setVisibility(View.GONE);
1004 | }
1005 | }
1006 | else if (REQUIRE_CALENDAR.equals(permission))
1007 | {
1008 | mName.setText("Calendar");
1009 | mImage.setImageResource(R.drawable.ic_calendar);
1010 |
1011 | setRequestPermissions(Manifest.permission.WRITE_CALENDAR);
1012 |
1013 | if (builder.getCalendarDescription() != null)
1014 | {
1015 | mMessage.setText(builder.getCalendarDescription());
1016 | mMessage.setVisibility(View.VISIBLE);
1017 | }
1018 | else
1019 | {
1020 | mMessage.setVisibility(View.GONE);
1021 | }
1022 | }
1023 | else if (REQUIRE_STORAGE.equals(permission))
1024 | {
1025 | mName.setText("Storage");
1026 | mImage.setImageResource(R.drawable.ic_folder);
1027 |
1028 | setRequestPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE);
1029 |
1030 | if (builder.getStorageDescription() != null)
1031 | {
1032 | mMessage.setText(builder.getStorageDescription());
1033 | mMessage.setVisibility(View.VISIBLE);
1034 | }
1035 | else
1036 | {
1037 | mMessage.setVisibility(View.GONE);
1038 | }
1039 | }
1040 | else if (REQUIRE_CAMERA.equals(permission))
1041 | {
1042 | mName.setText("Camera");
1043 | mImage.setImageResource(R.drawable.ic_camera);
1044 |
1045 | setRequestPermissions(Manifest.permission.CAMERA);
1046 |
1047 | if (builder.getCameraDescription() != null)
1048 | {
1049 | mMessage.setText(builder.getCameraDescription());
1050 | mMessage.setVisibility(View.VISIBLE);
1051 | }
1052 | else
1053 | {
1054 | mMessage.setVisibility(View.GONE);
1055 | }
1056 | }
1057 | else if (REQUIRE_AUDIO.equals(permission))
1058 | {
1059 | mName.setText("Audio");
1060 | mImage.setImageResource(R.drawable.ic_mic);
1061 |
1062 | setRequestPermissions(Manifest.permission.RECORD_AUDIO);
1063 |
1064 | if (builder.getAudioDescription() != null)
1065 | {
1066 | mMessage.setText(builder.getAudioDescription());
1067 | mMessage.setVisibility(View.VISIBLE);
1068 | }
1069 | else
1070 | {
1071 | mMessage.setVisibility(View.GONE);
1072 | }
1073 | }
1074 | else if (REQUIRE_LOCATION.equals(permission))
1075 | {
1076 | mName.setText("Location");
1077 | mImage.setImageResource(R.drawable.ic_location);
1078 |
1079 | setRequestPermissions(Manifest.permission.ACCESS_FINE_LOCATION);
1080 |
1081 | if (builder.getLocationDescription() != null)
1082 | {
1083 | mMessage.setText(builder.getLocationDescription());
1084 | mMessage.setVisibility(View.VISIBLE);
1085 | }
1086 | else
1087 | {
1088 | mMessage.setVisibility(View.GONE);
1089 | }
1090 | }
1091 | else
1092 | {
1093 |
1094 | }
1095 | }
1096 |
1097 | public void setRequestPermissions(final String requestPermission)
1098 | {
1099 | if (PermissionUtils.IsPermissionEnabled(mContext, requestPermission))
1100 | {
1101 | int color = ContextCompat.getColor(mContext, R.color.button_pressed);
1102 | mImage.setColorFilter(color);
1103 | if (optional) {
1104 | mButton.setText("Active");
1105 | mButton.setColor(ContextCompat.getColor(mContext, R.color.white), ContextCompat.getColor(mContext, R.color.white),
1106 | ContextCompat.getColor(mContext, R.color.green_light), ContextCompat.getColor(mContext, R.color.green), ContextCompat.getColor(mContext, R.color.green));
1107 | mButton.setButtonStatus(true);
1108 | mButton.setOnClickListener(new View.OnClickListener() {
1109 | @Override
1110 | public void onClick(View view) {
1111 | mButton.setButtonStatus(true);
1112 | }
1113 | });
1114 | }
1115 | }
1116 | else
1117 | {
1118 | int color = ContextCompat.getColor(mContext, R.color.button_inactive);
1119 | mImage.setColorFilter(color);
1120 | if (optional)
1121 | {
1122 | mButton.setOnClickListener(new View.OnClickListener() {
1123 | @Override
1124 | public void onClick(View view) {
1125 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
1126 | requestPermissions(new String[]{requestPermission}, REQUEST_PERMISSION);
1127 | }
1128 | }
1129 | });
1130 | }
1131 | }
1132 | }
1133 | }
1134 |
1135 | public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
1136 |
1137 | private int spanCount;
1138 | private int spacing;
1139 | private boolean includeEdge;
1140 |
1141 | public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
1142 | this.spanCount = spanCount;
1143 | this.spacing = spacing;
1144 | this.includeEdge = includeEdge;
1145 | }
1146 |
1147 | @Override
1148 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
1149 | int position = parent.getChildAdapterPosition(view); // item position
1150 | int column = position % spanCount; // item column
1151 |
1152 | if (includeEdge) {
1153 | outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
1154 | outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
1155 |
1156 | // if (position < spanCount) { // top edge
1157 | // outRect.top = spacing/2;
1158 | // }
1159 | outRect.bottom = spacing/2; // item bottom
1160 | } else {
1161 | outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
1162 | outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
1163 | if (position >= spanCount) {
1164 | outRect.top = spacing; // item top
1165 | }
1166 | }
1167 | }
1168 | }
1169 | }
1170 |
--------------------------------------------------------------------------------