Browse Source

Initial commit

main
James Fenn 3 years ago
commit
15b2c7f9bf
45 changed files with 1046 additions and 0 deletions
  1. +9
    -0
      .gitignore
  2. +1
    -0
      app/.gitignore
  3. +31
    -0
      app/build.gradle
  4. +25
    -0
      app/proguard-rules.pro
  5. +26
    -0
      app/src/androidTest/java/james/metronome/ExampleInstrumentedTest.java
  6. +23
    -0
      app/src/main/AndroidManifest.xml
  7. +267
    -0
      app/src/main/java/james/metronome/activities/MainActivity.java
  8. +30
    -0
      app/src/main/java/james/metronome/data/TickData.java
  9. +46
    -0
      app/src/main/java/james/metronome/utils/WhileHeldListener.java
  10. +62
    -0
      app/src/main/java/james/metronome/views/MetronomeView.java
  11. +14
    -0
      app/src/main/res/drawable/border_bottom.xml
  12. +14
    -0
      app/src/main/res/drawable/border_top.xml
  13. +9
    -0
      app/src/main/res/drawable/ic_less.xml
  14. +9
    -0
      app/src/main/res/drawable/ic_more.xml
  15. +9
    -0
      app/src/main/res/drawable/ic_note.xml
  16. +9
    -0
      app/src/main/res/drawable/ic_pause.xml
  17. +9
    -0
      app/src/main/res/drawable/ic_play.xml
  18. +62
    -0
      app/src/main/res/layout/activity_main.xml
  19. +40
    -0
      app/src/main/res/layout/item_tick.xml
  20. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher.png
  21. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher_round.png
  22. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher.png
  23. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher_round.png
  24. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher.png
  25. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
  26. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  27. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
  28. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  29. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
  30. BIN
      app/src/main/res/mipmap/ic_launcher_web.png
  31. BIN
      app/src/main/res/raw/beep.mp3
  32. BIN
      app/src/main/res/raw/click.mp3
  33. BIN
      app/src/main/res/raw/ding.mp3
  34. BIN
      app/src/main/res/raw/wood.mp3
  35. +10
    -0
      app/src/main/res/values-v21/styles.xml
  36. +11
    -0
      app/src/main/res/values-v24/styles.xml
  37. +8
    -0
      app/src/main/res/values/colors.xml
  38. +5
    -0
      app/src/main/res/values/strings.xml
  39. +9
    -0
      app/src/main/res/values/styles.xml
  40. +17
    -0
      app/src/test/java/james/metronome/ExampleUnitTest.java
  41. +23
    -0
      build.gradle
  42. +17
    -0
      gradle.properties
  43. +160
    -0
      gradlew
  44. +90
    -0
      gradlew.bat
  45. +1
    -0
      settings.gradle

+ 9
- 0
.gitignore View File

@@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.externalNativeBuild

+ 1
- 0
app/.gitignore View File

@@ -0,0 +1 @@
/build

+ 31
- 0
app/build.gradle View File

@@ -0,0 +1,31 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "james.metronome"
minSdkVersion 14
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support:design:25.3.1'
testCompile 'junit:junit:4.12'
}

+ 25
- 0
app/proguard-rules.pro View File

@@ -0,0 +1,25 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /home/james/Android/Sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

+ 26
- 0
app/src/androidTest/java/james/metronome/ExampleInstrumentedTest.java View File

@@ -0,0 +1,26 @@
package james.metronome;

import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

import static org.junit.Assert.*;

/**
* Instrumentation test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();

assertEquals("james.metronome", appContext.getPackageName());
}
}

+ 23
- 0
app/src/main/AndroidManifest.xml View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="james.metronome">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".activities.MainActivity"
android:hardwareAccelerated="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

+ 267
- 0
app/src/main/java/james/metronome/activities/MainActivity.java View File

@@ -0,0 +1,267 @@
package james.metronome.activities;

import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.content.SharedPreferences;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Build;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v4.animation.ValueAnimatorCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.util.Locale;

import james.metronome.R;
import james.metronome.data.TickData;
import james.metronome.utils.WhileHeldListener;
import james.metronome.views.MetronomeView;

public class MainActivity extends AppCompatActivity implements Runnable {

public static final String PREF_TICK = "tick";
public static final String PREF_INTERVAL = "interval";

public static final TickData[] ticks = new TickData[]{
new TickData("Beep", R.raw.beep),
new TickData("Click", R.raw.click),
new TickData("Ding", R.raw.ding),
new TickData("Wood", R.raw.wood)
};

private SoundPool soundPool;
private Handler handler;
private int soundId = -1;
private boolean isPlaying;
private boolean isExpanded;

private SharedPreferences prefs;
private int tick;
private int bpm;
private long interval;

private MetronomeView metronomeView;
private ImageView playView;
private TextView bpmView;
private LinearLayout ticksLayout;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
prefs = PreferenceManager.getDefaultSharedPreferences(this);

metronomeView = (MetronomeView) findViewById(R.id.metronome);
playView = (ImageView) findViewById(R.id.play);
bpmView = (TextView) findViewById(R.id.bpm);
View lessView = findViewById(R.id.less);
View moreView = findViewById(R.id.more);
ticksLayout = (LinearLayout) findViewById(R.id.ticks);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
soundPool = new SoundPool.Builder()
.setMaxStreams(1)
.setAudioAttributes(new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build())
.build();
} else soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);

tick = prefs.getInt(PREF_TICK, 0);
soundId = ticks[tick].getSoundId(this, soundPool);

interval = prefs.getLong(PREF_INTERVAL, 500);
bpm = toBpm(interval);
metronomeView.setInterval(interval);
bpmView.setText(String.format(Locale.getDefault(), getString(R.string.bpm), String.valueOf(bpm)));

handler = new Handler();

getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

playView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isPlaying)
pause();
else play();
}
});

moreView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bpm < 300)
setBpm(++bpm);
}
});

moreView.setOnTouchListener(new WhileHeldListener() {
@Override
public void onHeld() {
if (bpm < 300)
setBpm(++bpm);
}
});

lessView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bpm > 1)
setBpm(--bpm);
}
});

lessView.setOnTouchListener(new WhileHeldListener() {
@Override
public void onHeld() {
if (bpm > 1)
setBpm(--bpm);
}
});

LayoutInflater inflater = LayoutInflater.from(this);
for (int i = 0; i < ticks.length; i++) {
View v = inflater.inflate(R.layout.item_tick, ticksLayout, false);
v.setTag(i);
v.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = (int) v.getTag();
if (isExpanded) {
if (tick != position) {
tick = position;
soundId = ticks[tick].getSoundId(MainActivity.this, soundPool);
prefs.edit().putInt(PREF_TICK, tick).apply();

if (!isPlaying)
soundPool.play(soundId, 1.0f, 1.0f, 0, 0, 1.0f);
}

isExpanded = false;
for (int i = 0; i < ticksLayout.getChildCount(); i++) {
final View view = ticksLayout.getChildAt(i);

if (view.findViewById(R.id.background).getAlpha() == 1) {
ValueAnimator animator = ValueAnimator.ofObject(new ArgbEvaluator(), Color.WHITE, Color.BLACK);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
view.findViewById(R.id.background).setAlpha(1 - animation.getAnimatedFraction());
((TextView) view.findViewById(R.id.name)).setTextColor((int) animation.getAnimatedValue());
}
});
animator.start();

ValueAnimator animator2 = ValueAnimator.ofObject(new ArgbEvaluator(), Color.WHITE, ContextCompat.getColor(MainActivity.this, R.color.colorAccent));
animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
((ImageView) view.findViewById(R.id.image)).setColorFilter((int) animation.getAnimatedValue());
}
});
animator2.start();
}

if (tick != i)
view.setVisibility(View.GONE);
}
} else {
isExpanded = true;
for (int i = 0; i < ticksLayout.getChildCount(); i++) {
final View view = ticksLayout.getChildAt(i);
view.setVisibility(View.VISIBLE);
if (tick == i) {
ValueAnimator animator = ValueAnimator.ofObject(new ArgbEvaluator(), Color.BLACK, Color.WHITE);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
view.findViewById(R.id.background).setAlpha(animation.getAnimatedFraction());
((TextView) view.findViewById(R.id.name)).setTextColor((int) animation.getAnimatedValue());
}
});
animator.start();

ValueAnimator animator2 = ValueAnimator.ofObject(new ArgbEvaluator(), ContextCompat.getColor(MainActivity.this, R.color.colorAccent), Color.WHITE);
animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
((ImageView) view.findViewById(R.id.image)).setColorFilter((int) animation.getAnimatedValue());
}
});
animator2.start();
}
}
}
}
});

((TextView) v.findViewById(R.id.name)).setText(ticks[i].getName());
if (i != tick)
v.setVisibility(View.GONE);

ticksLayout.addView(v);
}
}

private static int toBpm(long interval) {
return (int) (60000 / interval);
}

private static long toInterval(int bpm) {
return (long) 60000 / bpm;
}

private void play() {
handler.post(this);
playView.setImageResource(R.drawable.ic_pause);
isPlaying = true;
}

private void pause() {
handler.removeCallbacks(this);
playView.setImageResource(R.drawable.ic_play);
isPlaying = false;
}

private void setBpm(int bpm) {
interval = toInterval(bpm);
metronomeView.setInterval(interval);
bpmView.setText(String.format(Locale.getDefault(), getString(R.string.bpm), String.valueOf(bpm)));

prefs.edit().putLong(PREF_INTERVAL, interval).apply();
}

@Override
protected void onStart() {
super.onStart();
}

@Override
protected void onStop() {
pause();
super.onStop();
}

@Override
public void run() {
if (isPlaying) {
soundPool.play(soundId, 1.0f, 1.0f, 0, 0, 1.0f);
handler.postDelayed(this, interval);
metronomeView.onTick();
}
}
}

+ 30
- 0
app/src/main/java/james/metronome/data/TickData.java View File

@@ -0,0 +1,30 @@
package james.metronome.data;

import android.content.Context;
import android.media.SoundPool;
import android.support.annotation.RawRes;

public class TickData {

private String name;
private int soundRes;

public TickData(String name, @RawRes int soundRes) {
this.name = name;
this.soundRes = soundRes;
}

public String getName() {
return name;
}

@RawRes
public int getSoundRes() {
return soundRes;
}

public int getSoundId(Context context, SoundPool pool) {
return pool.load(context, getSoundRes(), 1);
}

}

+ 46
- 0
app/src/main/java/james/metronome/utils/WhileHeldListener.java View File

@@ -0,0 +1,46 @@
package james.metronome.utils;

import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;

public abstract class WhileHeldListener implements View.OnTouchListener, Runnable {

private Handler handler;
private int interval;

public WhileHeldListener() {
interval = 100;
}

public WhileHeldListener(int interval) {
this.interval = interval;
}

public abstract void onHeld();

@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (handler != null)
return true;
handler = new Handler();
handler.postDelayed(this, interval);
break;
case MotionEvent.ACTION_UP:
if (handler == null)
return true;
handler.removeCallbacks(this);
handler = null;
break;
}
return false;
}

@Override
public void run() {
onHeld();
handler.postDelayed(this, interval);
}
}

+ 62
- 0
app/src/main/java/james/metronome/views/MetronomeView.java View File

@@ -0,0 +1,62 @@
package james.metronome.views;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.animation.DecelerateInterpolator;

public class MetronomeView extends View {

private Paint paint;
private long interval = 500;
private float distance;

public MetronomeView(Context context) {
this(context, null);
}

public MetronomeView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public MetronomeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);

paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1);
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
}

public void setInterval(long interval) {
this.interval = interval;
}

public void onTick() {
ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
animator.setDuration(interval);
animator.setInterpolator(new DecelerateInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
distance = (float) animation.getAnimatedValue();
invalidate();
}
});
animator.start();
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setAlpha((int) (255 * (1 - distance)));
canvas.drawCircle(canvas.getWidth() / 2, canvas.getHeight() / 2, distance * Math.max(canvas.getWidth(), canvas.getHeight()) / 2, paint);
}
}

+ 14
- 0
app/src/main/res/drawable/border_bottom.xml View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape>
<solid android:color="@color/colorBackground"/>
</shape>
</item>
<item android:gravity="bottom">
<shape>
<size android:height="1dip" />
<solid android:color="@color/colorBorder" />
</shape>
</item>
</layer-list>

+ 14
- 0
app/src/main/res/drawable/border_top.xml View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item>
<shape>
<solid android:color="@color/colorBackground"/>
</shape>
</item>
<item android:gravity="top">
<shape>
<size android:height="1dip" />
<solid android:color="@color/colorBorder" />
</shape>
</item>
</layer-list>

+ 9
- 0
app/src/main/res/drawable/ic_less.xml View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z"/>
</vector>

+ 9
- 0
app/src/main/res/drawable/ic_more.xml View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
</vector>

+ 9
- 0
app/src/main/res/drawable/ic_note.xml View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M12,3v10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55 -2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4V7h4V3h-6z"/>
</vector>

+ 9
- 0
app/src/main/res/drawable/ic_pause.xml View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
</vector>

+ 9
- 0
app/src/main/res/drawable/ic_play.xml View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M8,5v14l11,-7z"/>
</vector>

+ 62
- 0
app/src/main/res/layout/activity_main.xml View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<james.metronome.views.MetronomeView
android:id="@+id/metronome"
android:layout_width="match_parent"
android:layout_height="match_parent" />

<ImageView
android:id="@+id/play"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="centerInside"
app:srcCompat="@drawable/ic_play" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_gravity="bottom"
android:background="@drawable/border_top"
android:gravity="center">

<ImageView
android:id="@+id/less"
android:layout_width="56dp"
android:layout_height="56dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="centerInside"
app:srcCompat="@drawable/ic_less" />

<TextView
android:id="@+id/bpm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="120 BPM"
android:textColor="?android:textColorPrimary"
android:textSize="16sp" />

<ImageView
android:id="@+id/more"
android:layout_width="56dp"
android:layout_height="56dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:scaleType="centerInside"
app:srcCompat="@drawable/ic_more" />

</LinearLayout>

<LinearLayout
android:id="@+id/ticks"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:background="@drawable/border_bottom"
android:orientation="vertical" />

</FrameLayout>

+ 40
- 0
app/src/main/res/layout/item_tick.xml View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="?attr/selectableItemBackground">

<View
android:id="@+id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0"
android:background="@color/colorAccent" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="56dp"
android:gravity="center_vertical"
android:orientation="horizontal">

<ImageView
android:id="@+id/image"
android:layout_width="56dp"
android:layout_height="56dp"
android:scaleType="centerInside"
android:tint="@color/colorAccent"
app:srcCompat="@drawable/ic_note" />

<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:textColor="?android:textColorPrimary"
android:textSize="16sp" />

</LinearLayout>

</FrameLayout>

BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png View File

Before After
Width: 72  |  Height: 72  |  Size: 4.1 KiB

BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png View File

Before After
Width: 72  |  Height: 72  |  Size: 4.5 KiB

BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png View File

Before After
Width: 48  |  Height: 48  |  Size: 2.3 KiB

BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png View File

Before After
Width: 48  |  Height: 48  |  Size: 2.5 KiB

BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png View File

Before After
Width: 96  |  Height: 96  |  Size: 5.3 KiB

BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png View File

Before After
Width: 96  |  Height: 96  |  Size: 6.1 KiB

BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png View File

Before After
Width: 144  |  Height: 144  |  Size: 9.4 KiB

BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png View File

Before After
Width: 144  |  Height: 144  |  Size: 11 KiB

BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png View File

Before After
Width: 192  |  Height: 192  |  Size: 13 KiB

BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png View File

Before After
Width: 192  |  Height: 192  |  Size: 15 KiB

BIN
app/src/main/res/mipmap/ic_launcher_web.png View File

Before After
Width: 512  |  Height: 512  |  Size: 54 KiB

BIN
app/src/main/res/raw/beep.mp3 View File


BIN
app/src/main/res/raw/click.mp3 View File


BIN
app/src/main/res/raw/ding.mp3 View File


BIN
app/src/main/res/raw/wood.mp3 View File


+ 10
- 0
app/src/main/res/values-v21/styles.xml View File

@@ -0,0 +1,10 @@
<resources>

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:navigationBarColor">@color/colorPrimaryDark</item>
</style>

</resources>

+ 11
- 0
app/src/main/res/values-v24/styles.xml View File

@@ -0,0 +1,11 @@
<resources>

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:navigationBarColor">@color/colorPrimaryDark</item>
<item name="android:windowLightStatusBar">true</item>
</style>

</resources>

+ 8
- 0
app/src/main/res/values/colors.xml View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#F5F5F5</color>
<color name="colorPrimaryDark">#E0E0E0</color>
<color name="colorAccent">#FF5252</color>
<color name="colorBorder">#30000000</color>
<color name="colorBackground">#FAFAFA</color>
</resources>

+ 5
- 0
app/src/main/res/values/strings.xml View File

@@ -0,0 +1,5 @@
<resources>
<string name="app_name">Metronome</string>

<string name="bpm">%1$s BPM</string>
</resources>

+ 9
- 0
app/src/main/res/values/styles.xml View File

@@ -0,0 +1,9 @@
<resources>

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>

</resources>

+ 17
- 0
app/src/test/java/james/metronome/ExampleUnitTest.java View File

@@ -0,0 +1,17 @@
package james.metronome;

import org.junit.Test;

import static org.junit.Assert.*;

/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}

+ 23
- 0
build.gradle View File

@@ -0,0 +1,23 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

allprojects {
repositories {
jcenter()
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}

+ 17
- 0
gradle.properties View File

@@ -0,0 +1,17 @@
# Project-wide Gradle settings.

# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.

# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html

# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m

# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

+ 160
- 0
gradlew View File

@@ -0,0 +1,160 @@
#!/usr/bin/env bash

##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""

APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn ( ) {
echo "$*"
}

die ( ) {
echo
echo "$*"
echo
exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null

CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi

# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`

# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option

if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi

# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"

exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

+ 90
- 0
gradlew.bat View File

@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windowz variants

if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*
goto execute

:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega

+ 1
- 0
settings.gradle View File

@@ -0,0 +1 @@
include ':app'

Loading…
Cancel
Save