1
0
Fork 0

Updated app to use Kotlin!

This commit is contained in:
Jonathan Chan 2018-05-05 17:04:58 -07:00
parent 093d3e7499
commit 3c77b37cd7
29 changed files with 640 additions and 2783 deletions

4
.gitignore vendored
View File

@ -2,6 +2,7 @@
.gradle
/local.properties
/.idea/workspace.xml
/.idea/tasks.xml
/.idea/libraries
.DS_Store
/build
@ -10,4 +11,5 @@
gradle*
/gradle
/app/build
keystore.properties
keystore.properties
/app/release

View File

@ -1 +0,0 @@
QUOZ

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WizardSettings">
<option name="children">
<map>
<entry key="imageWizard">
<value>
<PersistentState />
</value>
</entry>
</map>
</option>
</component>
</project>

Binary file not shown.

View File

@ -0,0 +1,29 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
</code_scheme>
</component>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
<entry name="!?*.aj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>

View File

@ -1,3 +0,0 @@
<component name="CopyrightManager">
<settings default="" />
</component>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="PROJECT" charset="UTF-8" />
</component>
</project>

View File

@ -1,18 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="4">
<list size="5">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
</list>
</value>
</option>
@ -27,17 +25,7 @@
</value>
</option>
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
<option name="myNullables">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="4">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
</list>
</value>
</option>
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

File diff suppressed because it is too large Load Diff

View File

@ -1,32 +1,20 @@
apply plugin: 'com.android.application'
def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
apply plugin: "com.android.application"
apply plugin: "kotlin-android"
android {
signingConfigs {
config {
keyAlias 'AndroidKey'
keyPassword keystoreProperties['password']
storeFile file('C:/Users/Jonathan/Documents/Projects/Keystores/android.jks')
storePassword keystoreProperties['password']
}
}
compileSdkVersion 24
buildToolsVersion "24.0.1"
compileSdkVersion 27
buildToolsVersion "27.0.3"
defaultConfig {
applicationId "nonphatic.quoz"
minSdkVersion 19
targetSdkVersion 24
minSdkVersion 21
targetSdkVersion 27
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.config
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
debug {
}
@ -34,9 +22,12 @@ android {
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.1.1'
compile 'com.android.support:support-v4:24.1.1'
compile 'com.android.support:design:24.1.1'
implementation fileTree(include: ["*.jar"], dir: "libs")
implementation "com.android.support:appcompat-v7:27.1.1"
implementation "com.android.support:support-v4:27.1.1"
implementation "com.android.support:design:27.1.1"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
repositories {
mavenCentral()
}

View File

@ -1,13 +0,0 @@
package nonphatic.quoz;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@ -1,336 +0,0 @@
package nonphatic.quoz;
import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.preference.PreferenceManager;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import nonphatic.quoz.preferences.SettingsActivity;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
/**
* An example full-screen activity that shows and hides the system UI (i.e.
* status bar and navigation/system bar) with user interaction.
*/
public class MainActivity extends AppCompatActivity {
/**
* Whether or not the system UI should be auto-hidden after
* {@link #AUTO_HIDE_DELAY_MILLIS} milliseconds.
*/
private static final boolean AUTO_HIDE = true;
/**
* If {@link #AUTO_HIDE} is set, the number of milliseconds to wait after
* user interaction before hiding the system UI.
*/
private static final int AUTO_HIDE_DELAY_MILLIS = 3000;
/**
* Some older devices needs a small delay between UI widget updates
* and a change of the status and navigation bar.
*/
private static final int UI_ANIMATION_DELAY = 300;
private final Handler mHideHandler = new Handler();
private View mContentView;
private FloatingActionButton mFab;
private View mainView;
private Timer timer;
private TimerTask timerTask;
private MediaPlayer mediaPlayer;
private Random random;
private int saturationPercent;
private String changeMode;
private final Runnable mHidePart2Runnable = new Runnable() {
@SuppressLint("InlinedApi")
@Override
public void run() {
// Delayed removal of status and navigation bar
// Note that some of these constants are new as of API 16 (Jelly Bean)
// and API 19 (KitKat). It is safe to use them, as they are inlined
// at compile-time and do nothing on earlier devices.
mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
}
};
private final Runnable mShowPart2Runnable = new Runnable() {
@Override
public void run() {
// Delayed display of UI elements
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.show();
}
}
};
private final Runnable mHideRunnable = new Runnable() {
@Override
public void run() {
hide();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
getPreferences();
mContentView = findViewById(R.id.fullscreen_content);
mFab = (FloatingActionButton)findViewById(R.id.fab);
mainView = findViewById(R.id.main_layout);
timer = new Timer();
random = new Random();
// Set up the user interaction to manually show or hide the system UI.
mContentView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!isSwiping) {
setBackgroundToRandomColour();
}
}
});
mContentView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
ClipboardManager clipboardManager = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("QUOZ Colour", ((TextView)mContentView).getText());
clipboardManager.setPrimaryClip(clip);
Toast.makeText(getApplicationContext(), "Copied to clipboard", Toast.LENGTH_SHORT).show();
return true;
}
});
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Trigger the initial hide() shortly after the activity has been
// created, to briefly hint to the user that UI controls
// are available.
delayedHide(100);
}
private void toggle(float yDelta) {
if (yDelta < 0) {
hide();
} else {
show();
}
}
private void hide() {
// Hide UI first
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.hide();
}
mFab.hide();
// Schedule a runnable to remove the status and navigation bar after a delay
mHideHandler.removeCallbacks(mShowPart2Runnable);
mHideHandler.postDelayed(mHidePart2Runnable, UI_ANIMATION_DELAY);
}
@SuppressLint("InlinedApi")
private void show() {
// Show the system bar
mContentView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
mFab.show();
// Schedule a runnable to display UI elements after a delay
mHideHandler.removeCallbacks(mHidePart2Runnable);
mHideHandler.postDelayed(mShowPart2Runnable, UI_ANIMATION_DELAY);
}
/**
* Schedules a call to hide() in [delay] milliseconds, canceling any
* previously scheduled calls.
*/
private void delayedHide(int delayMillis) {
mHideHandler.removeCallbacks(mHideRunnable);
mHideHandler.postDelayed(mHideRunnable, delayMillis);
}
//region OVERRIDES
private float yPosOnDown;
private float yDelta;
private boolean isSwiping;
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
yPosOnDown = event.getY();
isSwiping = false;
break;
case MotionEvent.ACTION_UP:
if (isSwiping) {
toggle(yDelta);
}
break;
case MotionEvent.ACTION_MOVE:
float yPosCurr = event.getY();
yDelta = yPosCurr - yPosOnDown;
if (Math.abs(yDelta) > 120) {
isSwiping = true;
return true;
}
break;
}
return super.dispatchTouchEvent(event);
}
@Override
public void onResume() {
super.onResume();
getPreferences();
setBackgroundToRandomColour();
setTimer();
}
@Override
public void onPause() {
super.onPause();
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
}
//endregion
//region HELPERS
public void getPreferences() {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
saturationPercent = preferences.getInt("saturation", 30);
changeMode = preferences.getString("change_mode", "tap");
}
public void resetPreferences(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.edit().clear().commit();
PreferenceManager.setDefaultValues(context, R.xml.preferences, true);
Toast.makeText(getApplicationContext(), "Settings reset.", Toast.LENGTH_SHORT).show();
}
private void setBackgroundToRandomColour() {
float[] hsv = generateRandomColour();
float[] hsvText = new float[] { 0, 0, hsv[1] / 2 + 0.25f };
int randomColour = Color.HSVToColor(hsv);
int textColour = Color.HSVToColor(hsvText);
mainView.setBackgroundColor(randomColour);
((TextView)mContentView).setText(String.format("#%s", Integer.toHexString(randomColour).substring(2)));
((TextView)mContentView).setTextColor(textColour);
}
private float[] generateRandomColour() {
float phiRecip = Float.parseFloat(getResources().getText(R.string.phiRecip).toString());
return new float[] {
(random.nextFloat() + phiRecip) % 1 * 360, // random, nicely-spaced hue
saturationPercent / 100f, // saturation
1.0f // value
};
}
private void setTimer() {
if (timerTask != null) {
timerTask.cancel();
}
switch (changeMode) {
case "tap":
break;
case "cycle":
timerTask = new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
setBackgroundToRandomColour();
}
});
}
};
timer.schedule(timerTask, 0, 1000);
break;
case "leekspin":
timerTask = new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
setBackgroundToRandomColour();
}
});
}
};
timer.schedule(timerTask, 300, 550);
mediaPlayer = MediaPlayer.create(this, R.raw.ievan_polkka);
mediaPlayer.setLooping(true);
mediaPlayer.start();
break;
case "nyan":
timerTask = new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
setBackgroundToRandomColour();
}
});
}
};
timer.schedule(timerTask, 160, 420);
mediaPlayer = MediaPlayer.create(this, R.raw.nyan);
mediaPlayer.setLooping(true);
mediaPlayer.start();
break;
}
}
//endregion
//region ACTIVITIES
public void openSettings(View view) {
Intent intent = new Intent(this, SettingsActivity.class);
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, SettingsActivity.PreferencesFragment.class.getName());
intent.putExtra(SettingsActivity.EXTRA_NO_HEADERS, true);
startActivity(intent);
}
//endregion
}

View File

@ -0,0 +1,238 @@
package nonphatic.quoz
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.media.AudioManager
import android.media.MediaPlayer
import android.net.Uri
import android.preference.PreferenceManager
import android.support.design.widget.FloatingActionButton
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.preference.PreferenceActivity
import android.view.MotionEvent
import android.view.View
import android.widget.TextView
import android.widget.Toast
import nonphatic.quoz.preferences.SettingsActivity
import java.util.Random
import java.util.Timer
import java.util.TimerTask
class MainActivity : AppCompatActivity() {
private val mHideHandler: Handler = Handler()
private val random: Random = Random()
private val mediaPlayer: MediaPlayer = MediaPlayer()
private var timer: Timer = Timer()
private lateinit var mContentView: View
private lateinit var mFab: FloatingActionButton
private lateinit var mainView: View
private val saturationPercent
get() = PreferenceManager.getDefaultSharedPreferences(this)
.getInt(getString(R.string.preferences_colour_key),
resources.getInteger(R.integer.preferences_colour_default))
private val changeMode
get() = PreferenceManager.getDefaultSharedPreferences(this)
.getString(getString(R.string.preferences_mode_key),
getString(R.string.preferences_mode_default))
private val mHidePart2Runnable = Runnable {
// Delayed removal of status and navigation bar
// Note that some of these constants are new as of API 16 (Jelly Bean)
// and API 19 (KitKat). It is safe to use them, as they are inlined
// at compile-time and do nothing on earlier devices.
mContentView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LOW_PROFILE
or View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
}
private val mShowPart2Runnable = Runnable {
// Delayed display of UI elements
val actionBar = supportActionBar
actionBar?.show()
}
private val mHideRunnable = Runnable { hide() }
//region OVERRIDES
private var yPosOnDown: Float = 0f
private var yDelta: Float = 0f
private var isSwiping: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
volumeControlStream = AudioManager.STREAM_MUSIC
mContentView = findViewById(R.id.fullscreen_content)
mFab = findViewById(R.id.fab)
mainView = findViewById(R.id.main_layout)
// Set up the user interaction to manually show or hide the system UI.
mContentView.setOnClickListener {
if (!isSwiping) {
setColour()
}
}
mContentView.setOnLongClickListener {
val clipboardManager = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clip = ClipData.newPlainText(getString(R.string.app_name), (mContentView as TextView).text)
clipboardManager.primaryClip = clip
Toast.makeText(applicationContext, getString(R.string.toast_copied), Toast.LENGTH_SHORT).show()
true
}
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
// Trigger the initial hide() shortly after the activity has been
// created, to briefly hint to the user that UI controls
// are available.
mHideHandler.removeCallbacks(mHideRunnable)
mHideHandler.postDelayed(mHideRunnable, INIT_ANIMATION_DELAY)
}
private fun show() {
// Show the system bar
mContentView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
mFab.show()
// Schedule a runnable to display UI elements after a delay
mHideHandler.removeCallbacks(mHidePart2Runnable)
mHideHandler.postDelayed(mShowPart2Runnable, UI_ANIMATION_DELAY)
}
private fun hide() {
// Hide UI first
supportActionBar?.hide()
mFab.hide()
// Schedule a runnable to remove the status and navigation bar after a delay
mHideHandler.removeCallbacks(mShowPart2Runnable)
mHideHandler.postDelayed(mHidePart2Runnable, UI_ANIMATION_DELAY)
}
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
yPosOnDown = event.y
isSwiping = false
}
MotionEvent.ACTION_UP -> if (isSwiping) {
if (yDelta < 0) hide() else show()
}
MotionEvent.ACTION_MOVE -> {
val yPosCurr = event.y
yDelta = yPosCurr - yPosOnDown
if (Math.abs(yDelta) > SWIPE_MIN_DIST) {
isSwiping = true
return true
}
}
}
return super.dispatchTouchEvent(event)
}
public override fun onResume() {
super.onResume()
setColour()
setLoop()
}
public override fun onPause() {
super.onPause()
mediaPlayer.stop()
mediaPlayer.reset()
timer.cancel()
}
//endregion
//region HELPERS
private fun setColour(hsv: FloatArray = generateRandomColour()) {
val textHSV = floatArrayOf(0f, 0f, hsv[1] / 2 + 0.25f)
val backgroundColour = Color.HSVToColor(hsv)
val textColour = Color.HSVToColor(textHSV)
mainView.setBackgroundColor(backgroundColour)
(mContentView as TextView).text = "#${Integer.toHexString(backgroundColour and HEX_MASK)}"
(mContentView as TextView).setTextColor(textColour)
}
private fun generateRandomColour(): FloatArray = floatArrayOf(
(random.nextFloat() + PHI_RECIP) % 1 * 360, // random, nicely-spaced hue
saturationPercent / 100f, // saturation
1f // value
)
private fun scheduleTimer(delay: Long, period: Long) {
timer = Timer().apply {
schedule(object : TimerTask() {
override fun run() {
runOnUiThread {
setColour()
}
}
}, delay, period)
}
}
private fun playMedia(resId: Int) {
mediaPlayer.setDataSource(this, Uri.parse("android.resource://$packageName/$resId"))
mediaPlayer.prepare()
mediaPlayer.isLooping = true
mediaPlayer.start()
}
private fun setLoop() {
when (changeMode) {
"tap" -> {}
"cycle" -> {
scheduleTimer(0, 1000)
}
"leekspin" -> {
scheduleTimer(300, 550)
playMedia(R.raw.ievan_polkka)
}
"nyan" -> {
scheduleTimer(160, 423)
playMedia(R.raw.nyan)
}
}
}
//endregion
//region ACTIVITIES
fun openSettings(view: View) {
val intent = Intent(this, SettingsActivity::class.java)
intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, SettingsActivity.PreferencesFragment::class.java.name)
intent.putExtra(PreferenceActivity.EXTRA_NO_HEADERS, true)
startActivity(intent)
}
//endregion
companion object {
private const val UI_ANIMATION_DELAY = 300L
private const val INIT_ANIMATION_DELAY = 100L
private const val SWIPE_MIN_DIST = 120
private const val HEX_MASK = 0xFFFFFF
private const val PHI_RECIP = 0.6180339f
}
}

View File

@ -1,57 +0,0 @@
package nonphatic.quoz.preferences;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import nonphatic.quoz.R;
/**
* Created by Jonathan on 2016-08-07.
*/
public class AboutDialogPreference extends DialogPreference {
public AboutDialogPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public AboutDialogPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public AboutDialogPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AboutDialogPreference(Context context) {
super(context);
}
@Override
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
super.onPrepareDialogBuilder(builder);
builder.setNegativeButton(null, null);
builder.setPositiveButton(null, null);
builder.setTitle(null);
}
@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
ImageView nonphaticLogo = (ImageView)view.findViewById(R.id.nonphatic_logo);
nonphaticLogo.setOnLongClickListener(new View.OnLongClickListener() {
public boolean onLongClick(View view) {
Intent intent = new Intent(Intent.ACTION_VIEW);
//intent.setData(Uri.parse("market://search?q=pub:nonphatic"));
intent.setData(Uri.parse("https://github.com/nonphatic"));
getContext().startActivity(intent);
return true;
}
});
}
}

View File

@ -0,0 +1,44 @@
package nonphatic.quoz.preferences
import android.app.AlertDialog
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.preference.DialogPreference
import android.util.AttributeSet
import android.view.View
import android.widget.ImageView
import nonphatic.quoz.R
/**
* Created by Jonathan on 2016-08-07.
*/
class AboutDialogPreference : DialogPreference {
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}
constructor(context: Context) : super(context) {}
override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) {
super.onPrepareDialogBuilder(builder)
builder.setNegativeButton(null, null)
builder.setPositiveButton(null, null)
builder.setTitle(null)
}
override fun onBindDialogView(view: View) {
super.onBindDialogView(view)
val nonphaticLogo = view.findViewById<ImageView>(R.id.nonphatic_logo)
nonphaticLogo.setOnLongClickListener {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://github.com/nonphatic")
context.startActivity(intent)
true
}
}
}

View File

@ -1,109 +0,0 @@
package nonphatic.quoz.preferences;
import android.content.res.Configuration;
import android.os.Bundle;
import android.preference.PreferenceActivity;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.Toolbar;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls
* to be used with AppCompat.
*/
public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
private AppCompatDelegate mDelegate;
@Override
protected void onCreate(Bundle savedInstanceState) {
getDelegate().installViewFactory();
getDelegate().onCreate(savedInstanceState);
super.onCreate(savedInstanceState);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
getDelegate().onPostCreate(savedInstanceState);
}
public ActionBar getSupportActionBar() {
return getDelegate().getSupportActionBar();
}
public void setSupportActionBar(@Nullable Toolbar toolbar) {
getDelegate().setSupportActionBar(toolbar);
}
@Override
public MenuInflater getMenuInflater() {
return getDelegate().getMenuInflater();
}
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
@Override
public void setContentView(View view) {
getDelegate().setContentView(view);
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
getDelegate().setContentView(view, params);
}
@Override
public void addContentView(View view, ViewGroup.LayoutParams params) {
getDelegate().addContentView(view, params);
}
@Override
protected void onPostResume() {
super.onPostResume();
getDelegate().onPostResume();
}
@Override
protected void onTitleChanged(CharSequence title, int color) {
super.onTitleChanged(title, color);
getDelegate().setTitle(title);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
getDelegate().onConfigurationChanged(newConfig);
}
@Override
protected void onStop() {
super.onStop();
getDelegate().onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
getDelegate().onDestroy();
}
public void invalidateOptionsMenu() {
getDelegate().invalidateOptionsMenu();
}
private AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, null);
}
return mDelegate;
}
}

View File

@ -0,0 +1,96 @@
package nonphatic.quoz.preferences
import android.content.res.Configuration
import android.os.Bundle
import android.preference.PreferenceActivity
import android.support.annotation.LayoutRes
import android.support.v7.app.ActionBar
import android.support.v7.app.AppCompatDelegate
import android.support.v7.widget.Toolbar
import android.view.MenuInflater
import android.view.View
import android.view.ViewGroup
/**
* A [android.preference.PreferenceActivity] which implements and proxies the necessary calls
* to be used with AppCompat.
*/
abstract class AppCompatPreferenceActivity : PreferenceActivity() {
private var mDelegate: AppCompatDelegate? = null
val supportActionBar: ActionBar?
get() = delegate?.supportActionBar
private val delegate: AppCompatDelegate?
get() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, null)
}
return mDelegate
}
override fun onCreate(savedInstanceState: Bundle?) {
delegate?.installViewFactory()
delegate?.onCreate(savedInstanceState)
super.onCreate(savedInstanceState)
}
override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState)
delegate?.onPostCreate(savedInstanceState)
}
fun setSupportActionBar(toolbar: Toolbar?) {
delegate?.setSupportActionBar(toolbar)
}
override fun getMenuInflater(): MenuInflater? {
return delegate?.menuInflater
}
override fun setContentView(@LayoutRes layoutResID: Int) {
delegate?.setContentView(layoutResID)
}
override fun setContentView(view: View) {
delegate?.setContentView(view)
}
override fun setContentView(view: View, params: ViewGroup.LayoutParams) {
delegate?.setContentView(view, params)
}
override fun addContentView(view: View, params: ViewGroup.LayoutParams) {
delegate?.addContentView(view, params)
}
override fun onPostResume() {
super.onPostResume()
delegate?.onPostResume()
}
override fun onTitleChanged(title: CharSequence, color: Int) {
super.onTitleChanged(title, color)
delegate?.setTitle(title)
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
delegate?.onConfigurationChanged(newConfig)
}
override fun onStop() {
super.onStop()
delegate?.onStop()
}
override fun onDestroy() {
super.onDestroy()
delegate?.onDestroy()
}
override fun invalidateOptionsMenu() {
delegate?.invalidateOptionsMenu()
}
}

View File

@ -1,189 +0,0 @@
package nonphatic.quoz.preferences;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.DialogPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.support.v7.app.ActionBar;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.view.MenuItem;
import android.widget.Toast;
import nonphatic.quoz.R;
/**
* A {@link PreferenceActivity} that presents a set of application settings. On
* handset devices, settings are presented as a single list. On tablets,
* settings are split by category, with category headers shown to the left of
* the list of settings.
* <p/>
* See <a href="http://developer.android.com/design/patterns/settings.html">
* Android Design: Settings</a> for design guidelines and the <a
* href="http://developer.android.com/guide/topics/ui/settings.html">Settings
* API Guide</a> for more information on developing a Settings UI.
*/
public class SettingsActivity extends AppCompatPreferenceActivity {
/**
* A preference value change listener that updates the preference's summary
* to reflect its new value.
*/
private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object value) {
String stringValue = value.toString();
if (preference instanceof ListPreference) {
// For list preferences, look up the correct display value in
// the preference's 'entries' list.
ListPreference listPreference = (ListPreference) preference;
int index = listPreference.findIndexOfValue(stringValue);
// Set the summary to reflect the new value.
preference.setSummary(
index >= 0
? listPreference.getEntries()[index]
: null);
} else {
// For all other preferences, set the summary to the value's
// simple string representation.
preference.setSummary(stringValue);
}
return true;
}
};
/**
* Helper method to determine if the device has an extra-large screen. For
* example, 10" tablets are extra-large.
*/
private static boolean isXLargeTablet(Context context) {
return (context.getResources().getConfiguration().screenLayout
& Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
}
/**
* Binds a preference's summary to its value. More specifically, when the
* preference's value is changed, its summary (line of text below the
* preference title) is updated to reflect the value. The summary is also
* immediately updated upon calling this method. The exact display format is
* dependent on the type of preference.
*
* @see #sBindPreferenceSummaryToValueListener
*/
private static void bindPreferenceSummaryToValue(Preference preference) {
// Set the listener to watch for value changes.
preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
// Trigger the listener immediately with the preference's
// current value.
try {
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
PreferenceManager
.getDefaultSharedPreferences(preference.getContext())
.getString(preference.getKey(), ""));
}
catch (ClassCastException e) {
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
PreferenceManager
.getDefaultSharedPreferences(preference.getContext())
.getInt(preference.getKey(), 0));
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setupActionBar();
setVolumeControlStream(AudioManager.STREAM_MUSIC);
}
/**
* Set up the {@link android.app.ActionBar}, if the API is available.
*/
private void setupActionBar() {
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
// Show the Up button in the action bar.
actionBar.setDisplayHomeAsUpEnabled(true);
}
}
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
onBackPressed();
return true;
}
return super.onMenuItemSelected(featureId, item);
}
/**
* {@inheritDoc}
*/
@Override
public boolean onIsMultiPane() {
return isXLargeTablet(this);
}
/**
* This method stops fragment injection in malicious applications.
* Make sure to deny any unknown fragments here.
*/
protected boolean isValidFragment(String fragmentName) {
return PreferenceFragment.class.getName().equals(fragmentName)
|| PreferencesFragment.class.getName().equals(fragmentName);
}
/**
* This fragment shows colour type and change mode preferences.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class PreferencesFragment extends PreferenceFragment {
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
setHasOptionsMenu(true);
Preference resetButton = findPreference("reset_button");
resetButton.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
preferences.edit().clear().commit();
PreferenceManager.setDefaultValues(getActivity().getApplicationContext(), R.xml.preferences, true);
onStop();
onCreate(savedInstanceState);
Toast.makeText(getActivity().getApplicationContext(), "Settings reset.", Toast.LENGTH_SHORT).show();
return true;
}
});
bindPreferenceSummaryToValue(findPreference("change_mode"));
bindPreferenceSummaryToValue(findPreference("saturation"));
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
startActivity(new Intent(getActivity(), SettingsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
}
}

View File

@ -0,0 +1,174 @@
package nonphatic.quoz.preferences
import android.annotation.TargetApi
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.res.Configuration
import android.media.AudioManager
import android.os.Build
import android.os.Bundle
import android.preference.ListPreference
import android.preference.Preference
import android.preference.PreferenceActivity
import android.support.v7.app.ActionBar
import android.preference.PreferenceFragment
import android.preference.PreferenceManager
import android.view.MenuItem
import android.widget.Toast
import nonphatic.quoz.R
/**
* A [PreferenceActivity] that presents a set of application settings. On
* handset devices, settings are presented as a single list. On tablets,
* settings are split by category, with category headers shown to the left of
* the list of settings.
*
*
* See [
* Android Design: Settings](http://developer.android.com/design/patterns/settings.html) for design guidelines and the [Settings
* API Guide](http://developer.android.com/guide/topics/ui/settings.html) for more information on developing a Settings UI.
*/
class SettingsActivity : AppCompatPreferenceActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setupActionBar()
volumeControlStream = AudioManager.STREAM_MUSIC
}
/**
* Set up the [android.app.ActionBar], if the API is available.
*/
private fun setupActionBar() {
val actionBar = supportActionBar
actionBar?.setDisplayHomeAsUpEnabled(true)
}
override fun onMenuItemSelected(featureId: Int, item: MenuItem): Boolean {
val id = item.itemId
if (id == android.R.id.home) {
onBackPressed()
return true
}
return super.onMenuItemSelected(featureId, item)
}
/**
* {@inheritDoc}
*/
override fun onIsMultiPane(): Boolean {
return isXLargeTablet(this)
}
/**
* This method stops fragment injection in malicious applications.
* Make sure to deny any unknown fragments here.
*/
override fun isValidFragment(fragmentName: String): Boolean {
return PreferenceFragment::class.java.name == fragmentName || PreferencesFragment::class.java.name == fragmentName
}
/**
* This fragment shows colour type and change mode preferences.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
class PreferencesFragment : PreferenceFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
addPreferencesFromResource(R.xml.preferences)
setHasOptionsMenu(true)
val resetButton = findPreference(getString(R.string.preferences_reset_key))
resetButton.onPreferenceClickListener = Preference.OnPreferenceClickListener {
val preferences = PreferenceManager.getDefaultSharedPreferences(activity.applicationContext)
preferences.edit().clear().commit()
PreferenceManager.setDefaultValues(activity.applicationContext, R.xml.preferences, true)
onStop()
onCreate(savedInstanceState)
Toast.makeText(activity.applicationContext, getString(R.string.toast_reset), Toast.LENGTH_SHORT).show()
true
}
bindPreferenceSummaryToValue(findPreference(getString(R.string.preferences_mode_key)))
bindPreferenceSummaryToValue(findPreference(getString(R.string.preferences_colour_key)))
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId
if (id == android.R.id.home) {
startActivity(Intent(activity, SettingsActivity::class.java))
return true
}
return super.onOptionsItemSelected(item)
}
}
companion object {
/**
* A preference value change listener that updates the preference's summary
* to reflect its new value.
*/
private val sBindPreferenceSummaryToValueListener = Preference.OnPreferenceChangeListener { preference, value ->
val stringValue = value.toString()
if (preference is ListPreference) {
// For list preferences, look up the correct display value in
// the preference's 'entries' list.
val index = preference.findIndexOfValue(stringValue)
// Set the summary to reflect the new value.
preference.setSummary(
if (index >= 0)
preference.entries[index]
else
null)
} else {
// For all other preferences, set the summary to the value's
// simple string representation.
preference.summary = stringValue
}
true
}
/**
* Helper method to determine if the device has an extra-large screen. For
* example, 10" tablets are extra-large.
*/
private fun isXLargeTablet(context: Context): Boolean {
return context.resources.configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK >= Configuration.SCREENLAYOUT_SIZE_XLARGE
}
/**
* Binds a preference's summary to its value. More specifically, when the
* preference's value is changed, its summary (line of text below the
* preference title) is updated to reflect the value. The summary is also
* immediately updated upon calling this method. The exact display format is
* dependent on the type of preference.
*
* @see .sBindPreferenceSummaryToValueListener
*/
private fun bindPreferenceSummaryToValue(preference: Preference) {
// Set the listener to watch for value changes.
preference.onPreferenceChangeListener = sBindPreferenceSummaryToValueListener
// Trigger the listener immediately with the preference's
// current value.
try {
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
PreferenceManager
.getDefaultSharedPreferences(preference.context)
.getString(preference.key, ""))
} catch (e: ClassCastException) {
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
PreferenceManager
.getDefaultSharedPreferences(preference.context)
.getInt(preference.key, 0))
}
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -10,6 +10,6 @@
android:layout_height="wrap_content"
android:scaleType="fitStart"
android:adjustViewBounds="true"
android:src="@drawable/nonphatic_colour_light">
android:src="@drawable/nonphatic">
</ImageView>
</LinearLayout>

View File

@ -1,24 +1,13 @@
<resources>
<string name="app_name">QUOZ</string>
<string name="dummy_button">Dummy Button</string>
<string name="dummy_content">DUMMY\nCONTENT</string>
<string name="title_activity_settings">Settings</string>
<string name="phiRecip">0.618033988748984848</string>
<!-- Toast messages -->
<string name="toast_copied">Copied to clipboard.</string>
<string name="toast_reset">Settings reset.</string>
<!-- Strings related to Settings -->
<string-array name="preferences_colour_title">
<item>Pastel</item>
<item>Pastel neon</item>
<item>Neon</item>
</string-array>
<string-array name="preferences_colour_values">
<item>0.3</item>
<item>0.5</item>
<item>0.7</item>
</string-array>
<string name="preferences_mode_key">change_mode</string>
<string-array name="preferences_mode_title">
<item>Tap</item>
<item>Cycle</item>
@ -31,4 +20,10 @@
<item>leekspin</item>
<item>nyan</item>
</string-array>
<string name="preferences_mode_default">tap</string>
<string name="preferences_colour_key">saturation</string>
<integer name="preferences_colour_default">30</integer>
<string name="preferences_reset_key">reset_button</string>
</resources>

View File

@ -1,22 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SeekBarPreference
android:key="saturation"
android:key="@string/preferences_colour_key"
android:title="Saturation"
android:summary="Set saturation"
android:max="100"
android:defaultValue="30">
android:defaultValue="@integer/preferences_colour_default">
</SeekBarPreference>
<ListPreference
android:title="Change mode"
android:key="change_mode"
android:defaultValue="tap"
android:key="@string/preferences_mode_key"
android:defaultValue="@string/preferences_mode_default"
android:entries="@array/preferences_mode_title"
android:entryValues="@array/preferences_mode_values">
</ListPreference>
<Preference
android:title="Reset settings"
android:key="reset_button" />
android:key="@string/preferences_reset_key" />
<nonphatic.quoz.preferences.AboutDialogPreference
android:title="About"
android:key="about"

View File

@ -1,15 +0,0 @@
package nonphatic.quoz;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* To work on unit tests, switch the Test Artifact in the Build Variants view.
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}

View File

@ -1,11 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.2.41'
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.0'
classpath 'com.android.tools.build:gradle:3.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@ -15,6 +18,7 @@ buildscript {
allprojects {
repositories {
jcenter()
google()
}
}