diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..95ec4fe --- /dev/null +++ b/.gitignore @@ -0,0 +1,140 @@ +# Created by https://www.gitignore.io + +### Gradle ### +.gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + + +### Linux ### +*~ + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + + +### Windows ### +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + + +### Android ### +# Built application files +*.apk +*.ap_ + +# Files for the Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ + +# Gradle files +.gradle/ +build/ +/*/build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +### Android Patch ### +gen-external-apklibs + + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties + + +### Java ### +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..538a8a1 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,45 @@ +buildscript { + repositories { + mavenCentral() + jcenter() + } + + dependencies { + classpath 'com.jakewharton.hugo:hugo-plugin:1.2.1' + classpath 'com.github.ben-manes:gradle-versions-plugin:0.11.3' + } +} + +apply plugin: 'com.android.application' +apply plugin: 'com.jakewharton.hugo' +apply plugin: 'com.github.ben-manes.versions' + +android { + compileSdkVersion 22 + buildToolsVersion "22.0.1" + + defaultConfig { + applicationId "com.idlegandalf.ledd" + minSdkVersion 16 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile 'com.android.support:appcompat-v7:22.2.1' + compile 'com.android.support:support-v4:22.2.1' + compile 'com.squareup.okhttp:okhttp:2.4.0' + compile 'com.jakewharton:butterknife:7.0.1' + compile 'com.koushikdutta.async:androidasync:2.1.6' + compile 'com.android.support:design:22.2.1' + provided "org.projectlombok:lombok:1.16.4" +} diff --git a/app/proguard-rules.txt b/app/proguard-rules.txt new file mode 100644 index 0000000..e9ef993 --- /dev/null +++ b/app/proguard-rules.txt @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /home/giovanni/android-studio/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# 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 *; +#} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..0fcf6db --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/idlegandalf/ledd/AssistantActivity.java b/app/src/main/java/com/idlegandalf/ledd/AssistantActivity.java new file mode 100644 index 0000000..9a18c62 --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/AssistantActivity.java @@ -0,0 +1,73 @@ +package com.idlegandalf.ledd; + +import android.app.Fragment; +import android.app.FragmentTransaction; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.widget.FrameLayout; + +import com.idlegandalf.ledd.fragments.WelcomeFragment; + +import butterknife.Bind; +import butterknife.ButterKnife; + +public class AssistantActivity extends AppCompatActivity { + + @Bind(R.id.fragmentContainer) + FrameLayout container; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_asistent); + ButterKnife.bind(this); + + // Check that the activity is using the layout version with + // the fragment_container FrameLayout + if (container != null) { + + // However, if we're being restored from a previous state, + // then we don't need to do anything and should return or else + // we could end up with overlapping fragments. + if (savedInstanceState != null) { + return; + } + + // Create a new Fragment to be placed in the activity layout + WelcomeFragment welcomeFragment = new WelcomeFragment(); + + // In case this activity was started with special instructions from an + // Intent, pass the Intent's extras to the fragment as arguments + welcomeFragment.setArguments(getIntent().getExtras()); + + // Add the fragment to the 'fragment_container' FrameLayout + getFragmentManager().beginTransaction() + .add(R.id.fragmentContainer, welcomeFragment).commit(); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.welcome, menu); + return true; + } + + public void switchFragment(Fragment fragment) { + FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction(); + + fragmentTransaction.replace(R.id.fragmentContainer, fragment); + fragmentTransaction.addToBackStack(null); + + fragmentTransaction.commit(); + } + + @Override + public void onBackPressed() { + if (getFragmentManager().getBackStackEntryCount() > 0) { + getFragmentManager().popBackStack(); + } else { + super.onBackPressed(); + } + } +} diff --git a/app/src/main/java/com/idlegandalf/ledd/ColorActivity.java b/app/src/main/java/com/idlegandalf/ledd/ColorActivity.java new file mode 100644 index 0000000..2020457 --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/ColorActivity.java @@ -0,0 +1,147 @@ +package com.idlegandalf.ledd; + +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBarDrawerToggle; +import android.support.v7.app.AppCompatActivity; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; + +import com.idlegandalf.ledd.components.LEDDHelper; +import com.idlegandalf.ledd.components.RGBStripe; +import com.idlegandalf.ledd.components.StripeGroup; +import com.idlegandalf.ledd.utils.ColorPicker; + +import butterknife.Bind; +import butterknife.ButterKnife; + +public class ColorActivity extends AppCompatActivity { + + @Bind(R.id.colorPicker) + ColorPicker mWheel; + @Bind(R.id.drawer_layout) + DrawerLayout mDrawerLayout; + private ActionBarDrawerToggle mDrawerToggle; + private LEDDHelper mAPI; + private RGBStripe mActiveStripe; + private StripeGroup mActiveGroup; + private boolean firstRun = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_color); + ButterKnife.bind(this); + + // check for connectivity + ConnectivityManager connMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo networkInfo = connMgr.getActiveNetworkInfo(); + if (networkInfo == null || !networkInfo.isConnected()) { + // TODO: Display error + } + + mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.app_name, R.string.app_name) { + + /** Called when a drawer has settled in a completely closed state. */ + public void onDrawerClosed(View view) { + // TODO: do things that have to be done here + } + + /** Called when a drawer has settled in a completely open state. */ + public void onDrawerOpened(View drawerView) { + // TODO: do things that have to be done here + } + }; + + // set the drawer toggle as the DrawerListener + mDrawerLayout.setDrawerListener(mDrawerToggle); + + // enable Homebutton navigation to drawer + //getSupportActionBar().setDisplayHomeAsUpEnabled(true); + //getSupportActionBar().setHomeButtonEnabled(true); + // getSupportActionBar().setIcon(R.drawable.ic_bar); + + if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean( + "firstRun", true)) { + System.out.println("first run!"); + firstRun = true; + Intent intent = new Intent(this, AssistantActivity.class); + startActivity(intent); + } + + if (ColorApplication.getInstance().isAPIActive()) { + this.mAPI = ColorApplication.getInstance().getAPI(); + System.out.println("API active!"); + } else { + System.out.println("API not active :("); + this.finish(); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.color, menu); + return true; + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + // Sync the toggle state after onRestoreInstanceState has occurred. + mDrawerToggle.syncState(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + mDrawerToggle.onConfigurationChanged(newConfig); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Pass the event to ActionBarDrawerToggle, if it returns + // true, then it has handled the app icon touch event + if (mDrawerToggle.onOptionsItemSelected(item)) { + return true; + } + // Handle your other action bar items... + + switch (item.getItemId()) { + case R.id.action_settings: + Intent intent = new Intent(this, SettingsActivity.class); + startActivity(intent); + return super.onOptionsItemSelected(item); + } + + return super.onOptionsItemSelected(item); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_MENU) { + if (mDrawerLayout.isDrawerOpen(Gravity.LEFT)) { + mDrawerLayout.closeDrawer(Gravity.LEFT); + } else { + mDrawerLayout.openDrawer(Gravity.LEFT); + } + + return true; + } + return super.onKeyDown(keyCode, event); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + } +} diff --git a/app/src/main/java/com/idlegandalf/ledd/ColorApplication.java b/app/src/main/java/com/idlegandalf/ledd/ColorApplication.java new file mode 100644 index 0000000..2ce0b90 --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/ColorApplication.java @@ -0,0 +1,55 @@ +package com.idlegandalf.ledd; + +import android.app.Application; +import android.preference.PreferenceManager; + +import com.idlegandalf.ledd.components.LEDDHelper; + +import java.io.IOException; + +public class ColorApplication extends Application { + private static ColorApplication singleton; + private LEDDHelper mAPI = null; + private boolean mAPIActive = false; + + public static ColorApplication getInstance() { + return singleton; + } + + @Override + public void onCreate() { + super.onCreate(); + singleton = this; + try { + initAPI(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public LEDDHelper getAPI() { + return this.mAPI; + } + + public void initAPI() throws IOException { + if (PreferenceManager.getDefaultSharedPreferences(this) + .contains("pref_key_host")) { + + mAPI = new LEDDHelper(PreferenceManager + .getDefaultSharedPreferences(this).getString( + "pref_key_host", ""), + PreferenceManager + .getDefaultSharedPreferences(this).getInt( + "pref_key_port", 8825), + getApplicationContext() + ); + + this.mAPIActive = true; + System.out.println("api is declared as active now"); + } + } + + public boolean isAPIActive() { + return mAPIActive; + } +} diff --git a/app/src/main/java/com/idlegandalf/ledd/ColorService.java b/app/src/main/java/com/idlegandalf/ledd/ColorService.java new file mode 100644 index 0000000..c5c137f --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/ColorService.java @@ -0,0 +1,99 @@ +package com.idlegandalf.ledd; + +import android.app.Service; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.Process; +import android.widget.Toast; + +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketException; + +public class ColorService extends Service { + private Looper mServiceLooper; + private ServiceHandler mServiceHandler; + private DatagramSocket socket; + + // Handler that receives messages from the thread + private final class ServiceHandler extends Handler { + public ServiceHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + Bundle msgdata = msg.getData(); + + if (msgdata != null) { + try { + byte[] data = msgdata.getString("json").getBytes(); + DatagramPacket packet = new DatagramPacket(data, data.length, + InetAddress.getByName(msgdata.getString("ip")), msgdata.getInt("port")); + socket.send(packet); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // Stop the service using the startId, so that we don't stop + // the service in the middle of handling another job + //stopSelf(msg.arg1); + } + } + + @Override + public void onCreate() { + Toast.makeText(this, "ColorService started", Toast.LENGTH_SHORT).show(); + + try { + socket = new DatagramSocket(); + } catch (SocketException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // Start up the thread running the service. Note that we create a + // separate thread because the service normally runs in the process's + // main thread, which we don't want to block. We also make it + // background priority so CPU-intensive work will not disrupt our UI. + HandlerThread thread = new HandlerThread("ServiceStartArguments", + Process.THREAD_PRIORITY_BACKGROUND); + thread.start(); + + // Get the HandlerThread's Looper and use it for our Handler + mServiceLooper = thread.getLooper(); + mServiceHandler = new ServiceHandler(mServiceLooper); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + // For each start request, send a message to start a job and deliver the + // start ID so we know which request we're stopping when we finish the + // job + Message msg = mServiceHandler.obtainMessage(); + msg.arg1 = startId; + msg.setData(intent.getExtras()); + mServiceHandler.sendMessage(msg); + + // If we get killed, after returning from here, restart + return START_STICKY; + } + + @Override + public IBinder onBind(Intent intent) { + // We don't provide binding, so return null + return null; + } + + @Override + public void onDestroy() { + socket.close(); + Toast.makeText(this, "ColorService stopped", Toast.LENGTH_SHORT).show(); + } +} diff --git a/app/src/main/java/com/idlegandalf/ledd/DrawerFragment.java b/app/src/main/java/com/idlegandalf/ledd/DrawerFragment.java new file mode 100644 index 0000000..5187e2a --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/DrawerFragment.java @@ -0,0 +1,16 @@ +package com.idlegandalf.ledd; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +public class DrawerFragment extends Fragment { + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + return inflater.inflate(R.layout.drawer_fragment, container, false); + } +} diff --git a/app/src/main/java/com/idlegandalf/ledd/ProgressBarActionBarActivity.java b/app/src/main/java/com/idlegandalf/ledd/ProgressBarActionBarActivity.java new file mode 100644 index 0000000..cfa606e --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/ProgressBarActionBarActivity.java @@ -0,0 +1,32 @@ +package com.idlegandalf.ledd; + +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ProgressBar; + +public abstract class ProgressBarActionBarActivity extends AppCompatActivity { + private ProgressBar mProgressBar; + + public void setContentView(View view) { + init().addView(view); + } + + public void setContentView(int layoutResID) { + getLayoutInflater().inflate(layoutResID, init(), true); + } + + public void setContentView(View view, ViewGroup.LayoutParams params) { + init().addView(view, params); + } + + private ViewGroup init() { + super.setContentView(R.layout.progress); + mProgressBar = (ProgressBar) findViewById(R.id.activity_bar); + return (ViewGroup) findViewById(R.id.activity_frame); + } + + protected ProgressBar getProgressBar() { + return mProgressBar; + } +} diff --git a/app/src/main/java/com/idlegandalf/ledd/SettingsActivity.java b/app/src/main/java/com/idlegandalf/ledd/SettingsActivity.java new file mode 100644 index 0000000..3f06731 --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/SettingsActivity.java @@ -0,0 +1,17 @@ +package com.idlegandalf.ledd; + +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; + +public class SettingsActivity extends ActionBarActivity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + // Display the fragment as the main content. + getFragmentManager().beginTransaction() + .replace(android.R.id.content, new SettingsFragment()) + .commit(); + } +} diff --git a/app/src/main/java/com/idlegandalf/ledd/SettingsFragment.java b/app/src/main/java/com/idlegandalf/ledd/SettingsFragment.java new file mode 100644 index 0000000..0fe6fe8 --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/SettingsFragment.java @@ -0,0 +1,14 @@ +package com.idlegandalf.ledd; + +import android.os.Bundle; +import android.preference.PreferenceFragment; + +public class SettingsFragment extends PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Load the preferences from an XML resource + addPreferencesFromResource(R.xml.settings); + } +} diff --git a/app/src/main/java/com/idlegandalf/ledd/components/Controller.java b/app/src/main/java/com/idlegandalf/ledd/components/Controller.java new file mode 100644 index 0000000..3192bf4 --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/components/Controller.java @@ -0,0 +1,13 @@ +package com.idlegandalf.ledd.components; + +import java.util.ArrayList; + +import lombok.Getter; +import lombok.Setter; + + +@Getter +@Setter +public class Controller { + private ArrayList stripes; +} diff --git a/app/src/main/java/com/idlegandalf/ledd/components/HSV.java b/app/src/main/java/com/idlegandalf/ledd/components/HSV.java new file mode 100644 index 0000000..8cf72f6 --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/components/HSV.java @@ -0,0 +1,12 @@ +package com.idlegandalf.ledd.components; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class HSV { + private float hue; + private float saturation; + private float value; +} diff --git a/app/src/main/java/com/idlegandalf/ledd/components/Host.java b/app/src/main/java/com/idlegandalf/ledd/components/Host.java new file mode 100644 index 0000000..6f98b79 --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/components/Host.java @@ -0,0 +1,27 @@ +package com.idlegandalf.ledd.components; + + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class Host { + private String address; + private int port; + + public Host(String address, int port) { + this.address = address; + this.port = port; + } + + public Host() { + this.address = null; + this.port = 0; + } + + @Override + public String toString() { + return address + ":" + port; + } +} diff --git a/app/src/main/java/com/idlegandalf/ledd/components/LEDDHelper.java b/app/src/main/java/com/idlegandalf/ledd/components/LEDDHelper.java new file mode 100644 index 0000000..85f06b1 --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/components/LEDDHelper.java @@ -0,0 +1,68 @@ +package com.idlegandalf.ledd.components; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +import com.idlegandalf.ledd.ColorService; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +public class LEDDHelper { + private String mAddr; + private int mPort; + private Context context; + + final String ACTION_SETCOLOR = "set_color"; + final String ACTION_GETCOLOR = "get_color"; + + public LEDDHelper(String ip, int port, Context appl) throws UnsupportedEncodingException, + IOException { + this.mAddr = ip; + this.mPort = port; + this.context = appl; + } + + /** + * Send RGB values to dameon using the stripeid + * @param sid Stripeid + * @param color Color in HSV format + * @throws JSONException not valid json + * @throws IOException socket error + */ + public void sendColor(int sid, HSV color) + throws JSONException, IOException { + JSONObject jnson = new JSONObject(); + JSONArray hsv = new JSONArray(); + + hsv.put(color.getHue()); + hsv.put(color.getSaturation()); + hsv.put(color.getValue()); + + jnson.put("action", ACTION_SETCOLOR); + jnson.put("sid", sid); + jnson.put("color", hsv); + + sendJSON(jnson.toString()); + } + + private void sendJSON(String json) throws IOException { + Intent intent = new Intent(context, ColorService.class); + Bundle bndl = new Bundle(); + bndl.putString("json", json); + bndl.putString("host", this.mAddr); + bndl.putInt("port", mPort); + intent.putExtras(bndl); + context.startService(intent); + } + + public void onDestroy() { + Intent intent = new Intent(context, ColorService.class); + context.stopService(intent); + } +} diff --git a/app/src/main/java/com/idlegandalf/ledd/components/RGBStripe.java b/app/src/main/java/com/idlegandalf/ledd/components/RGBStripe.java new file mode 100644 index 0000000..d964868 --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/components/RGBStripe.java @@ -0,0 +1,17 @@ +package com.idlegandalf.ledd.components; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class RGBStripe { + private int id; + private int channelRed; + private int channelGreen; + private int channelBlue; + private String nice_name; + private HSV color; + private double gammaCorrection; + +} \ No newline at end of file diff --git a/app/src/main/java/com/idlegandalf/ledd/components/StripeGroup.java b/app/src/main/java/com/idlegandalf/ledd/components/StripeGroup.java new file mode 100644 index 0000000..d625d53 --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/components/StripeGroup.java @@ -0,0 +1,62 @@ +package com.idlegandalf.ledd.components; + + +import org.json.JSONException; + +import java.io.IOException; +import java.util.ArrayList; + +public class StripeGroup { + private String mNiceName; + private String mDescription; + private ArrayList mStripes; + + public String getNiceName() { + return mNiceName; + } + + public void setNiceName(String mNiceName) { + this.mNiceName = mNiceName; + } + + public String getDescription() { + return mDescription; + } + + public void setDescription(String mDescription) { + this.mDescription = mDescription; + } + + public RGBStripe getStripeByPos(int pos) { + return mStripes.get(pos); + } + + public RGBStripe getStripeById(int sid) { + for (int i = 0; i < mStripes.size(); i++) { + if (mStripes.get(i).getId() == sid) { + return mStripes.get(i); + } + } + return null; + } + + public void setRGB(int r, int g, int b, LEDDHelper api) throws JSONException, IOException { + for (int i = 0; i < mStripes.size(); i++) { + //mStripes.get(i).setRGB(r, g, b, api); + } + } + + public int addStripe(RGBStripe stripe) { + mStripes.add(stripe); + return mStripes.size()-1; + } + + public void removeStripeByObject(RGBStripe stripe) { + for (int i = 0; i < mStripes.size(); i++) { + if (mStripes.get(i).getId() == stripe.getId()) { + mStripes.remove(i); + return; + } + } + } +} diff --git a/app/src/main/java/com/idlegandalf/ledd/fragments/StepOneFragment.java b/app/src/main/java/com/idlegandalf/ledd/fragments/StepOneFragment.java new file mode 100644 index 0000000..91491af --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/fragments/StepOneFragment.java @@ -0,0 +1,233 @@ +package com.idlegandalf.ledd.fragments; + +import android.app.Fragment; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.design.widget.Snackbar; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.Toast; + +import com.idlegandalf.ledd.R; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.util.ArrayList; + +import butterknife.Bind; +import butterknife.ButterKnife; + +public class StepOneFragment extends Fragment { + @Bind(R.id.step2_back) + Button mBack; + @Bind(R.id.ipSelect) + EditText mAutoIP; + @Bind(R.id.step2_next) + Button mTest; + @Bind(R.id.portSelect) + EditText mAutoPort; + ArrayList mIPList = new ArrayList<>(); + boolean isAutoDetected = false; + + + public static StepOneFragment newInstance(String addrs) { + Bundle args = new Bundle(); + + args.putString("ips", addrs); + + StepOneFragment fragment = new StepOneFragment(); + fragment.setArguments(args); + return fragment; + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_step1, container, false); + ButterKnife.bind(this, v); + + return v; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + String ips = getArguments().getString("ips"); + + if (ips != null) { + if (!ips.isEmpty()) { + for (String string : ips.split(";")) { + if (string.isEmpty()) { + System.out.println("added addr " + string); + mIPList.add(string); + } + } + if (mIPList.size() > 0) isAutoDetected = true; + } + } + + mBack.setOnClickListener(nextClickListener); + mTest.setOnClickListener(nextClickListener); + } + + private OnClickListener nextClickListener = new OnClickListener() { + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.step2_back: + getActivity().onBackPressed(); + break; + case R.id.step2_next: + System.out.println("forward calling!"); + testController(); + break; + default: + break; + } + } + }; + + private void testController() { + ConnectivityManager connManager = (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo mWifi = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + + if (mWifi.isConnected()) { + setInputState(false); + if (!isValidAddrInput(mAutoIP, mAutoPort)) { + setInputState(true); + return; + } + getActivity().setProgressBarIndeterminateVisibility(true); + getActivity().setProgressBarVisibility(true); + new pokeController().execute(mAutoIP.toString(), mAutoPort.toString()); + } else { + Toast.makeText(getActivity().getApplicationContext(), "please connect WiFi", Toast.LENGTH_LONG).show(); + } + } + + private class pokeController extends AsyncTask { + protected Boolean doInBackground(String... ippo) { + try { + InetAddress controller = InetAddress.getByName(ippo[0]); + int port = Integer.parseInt(ippo[1]); + + DatagramSocket socket = new DatagramSocket(); + String hstr = new JSONObject().put("action", 5).toString(); + + DatagramPacket hello = new DatagramPacket(hstr.getBytes(), hstr.getBytes().length); + + hello.setPort(port); + socket.setSoTimeout(100); + + if (!controller.isReachable(50)) { + socket.close(); + return false; + } + + hello.setAddress(controller); + socket.send(hello); + + DatagramPacket packet = new DatagramPacket(new byte[1024], 1024); + try { + socket.receive(packet); + } catch (SocketTimeoutException e) { + socket.close(); + return false; + } + + Toast.makeText(getActivity().getApplicationContext(), new String(packet.getData(), 0, packet.getLength()), Toast.LENGTH_LONG).show(); + + JSONObject resjson = new JSONObject(new String(packet.getData(), 0, packet.getLength())); + + if (resjson.getInt("action") != 5) { + socket.close(); + return false; + } + + socket.close(); + } catch (SocketException e1) { + return false; + } catch (IOException e1) { + return false; + } catch (JSONException e) { + return false; + } + + return true; + } + + protected void onProgressUpdate(Void... progress) { + + } + + protected void onPostExecute(Boolean result) { + getActivity().setProgressBarIndeterminateVisibility(false); + getActivity().setProgressBarVisibility(false); + setInputState(true); + if (result) { + Snackbar.make(getActivity().findViewById(R.id.linlayp), "found daemon running on " + + "address " + mAutoIP.toString() + ":" + mAutoPort.toString(), Snackbar.LENGTH_LONG).show(); + } else { + Snackbar.make(getActivity().findViewById(R.id.linlayp), "No running daemon " + "found", Snackbar.LENGTH_LONG).show(); + } + } + } + + private boolean isValidIPFromEditText(EditText atv) { + String exip[] = atv.toString().trim().split("\\."); + if (exip.length != 4) return false; + + for (String string : exip) { + if (!string.isEmpty()) { + try { + if (Integer.parseInt(string) < 0 || Integer.parseInt(string) > 255) return false; + } catch (NumberFormatException e) { + return false; + } + } + } + + return true; + } + + private boolean isValidPortFromEditText(EditText atv) { + return !atv.toString().isEmpty() && Integer.parseInt(atv.toString()) > 1023 && Integer.parseInt(atv.toString()) <= 65535; + } + + private boolean isValidAddrInput(EditText ipatv, EditText portatv) { + if (!isValidIPFromEditText(ipatv)) { + Snackbar.make(getActivity().findViewById(R.id.linlayp), "Please enter valid IP " + "address", Snackbar.LENGTH_LONG).show(); + + return false; + } + if (!isValidPortFromEditText(portatv)) { + Snackbar.make(getActivity().findViewById(R.id.linlayp), "Please enter valid port " + "(1024-65535)", Snackbar.LENGTH_LONG).show(); + + return false; + } + return true; + } + + private void setInputState(boolean state) { + mAutoIP.setEnabled(state); + mAutoPort.setEnabled(state); + mBack.setEnabled(state); + mTest.setEnabled(state); + } +} diff --git a/app/src/main/java/com/idlegandalf/ledd/fragments/StepStripesFragment.java b/app/src/main/java/com/idlegandalf/ledd/fragments/StepStripesFragment.java new file mode 100644 index 0000000..1e8ecc5 --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/fragments/StepStripesFragment.java @@ -0,0 +1,7 @@ +package com.idlegandalf.ledd.fragments; + +import android.app.Fragment; + +public class StepStripesFragment extends Fragment { + +} diff --git a/app/src/main/java/com/idlegandalf/ledd/fragments/WelcomeFragment.java b/app/src/main/java/com/idlegandalf/ledd/fragments/WelcomeFragment.java new file mode 100644 index 0000000..b63ac5c --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/fragments/WelcomeFragment.java @@ -0,0 +1,218 @@ +package com.idlegandalf.ledd.fragments; + +import android.app.Fragment; +import android.content.Context; +import android.net.nsd.NsdManager; +import android.net.nsd.NsdServiceInfo; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.idlegandalf.ledd.AssistantActivity; +import com.idlegandalf.ledd.R; +import com.idlegandalf.ledd.components.Host; + +import java.net.Inet4Address; +import java.util.ArrayList; + +import butterknife.Bind; +import butterknife.ButterKnife; + +public class WelcomeFragment extends Fragment { + + @Bind(R.id.step1_auto) + Button mAuto; + @Bind(R.id.step1_manual) + Button mManual; + @Bind(R.id.host_container) + LinearLayout hostContainer; + @Bind(R.id.toolbar) + Toolbar toolbar; + + NsdManager mNsdManager; + final String TAG = "ColorD"; + NsdManager.DiscoveryListener mDiscoveryListener; + NsdManager.ResolveListener mResolveListener; + ArrayList hosts; + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.fragment_welcome, container, false); + ButterKnife.bind(this, v); + return v; + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + mAuto.setOnClickListener(nextClickListener); + mManual.setOnClickListener(nextClickListener); + + ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); + + hosts = new ArrayList<>(); + + mResolveListener = new NsdManager.ResolveListener() { + + @Override + public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) { + // Called when the resolve fails. Use the error code to debug. + Log.e(TAG, "Resolve failed" + errorCode); + } + + @Override + public void onServiceResolved(final NsdServiceInfo serviceInfo) { + Log.e(TAG, "Resolve Succeeded. " + serviceInfo); + + if (serviceInfo.getHost() instanceof Inet4Address) { + Log.d(TAG, "Found IPv4! Yay!"); + + final Host fHost = new Host(serviceInfo.getHost().getHostAddress(), serviceInfo.getPort()); + + if (!hosts.contains(fHost)) { + getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + View v = ((LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout + .host_row, hostContainer, false); + + TextView host = (TextView) v.findViewById(R.id.welcome_host); + + host.setText(serviceInfo.getHost().getHostAddress() + ":" + serviceInfo.getPort()); + host.setTag(serviceInfo); + + View.OnClickListener listener = new View.OnClickListener() { + @Override + public void onClick(View v) { + StepOneFragment stepOneFragment = StepOneFragment.newInstance(hosts.get((int) v.getTag()).toString()); + ((AssistantActivity) getActivity()).switchFragment(stepOneFragment); + } + }; + + v.setOnClickListener(listener); + host.setOnClickListener(listener); + + v.setTag(hosts.size()); + host.setTag(hosts.size()); + + hostContainer.addView(v); + hosts.add(fHost); + } + }); + } + } + } + }; + + mDiscoveryListener = new NsdManager.DiscoveryListener() { + + // Called as soon as service discovery begins. + @Override + public void onDiscoveryStarted(String regType) { + Log.d(TAG, "Service discovery started"); + } + + @Override + public void onServiceFound(NsdServiceInfo service) { + // A service was found! Do something with it. + Log.d(TAG, "Service discovery success " + service); + if (!service.getServiceType().equals("_ledd._tcp.")) { + // Service type is the string containing the protocol and + // transport layer for this service. + Log.d(TAG, "Unknown Service Type: " + service.getServiceType()); + } else { + mNsdManager.resolveService(service, mResolveListener); + } + } + + @Override + public void onServiceLost(NsdServiceInfo service) { + // When the network service is no longer available. + // Internal bookkeeping code goes here. + Log.e(TAG, "service lost" + service); + } + + @Override + public void onDiscoveryStopped(String serviceType) { + Log.i(TAG, "Discovery stopped: " + serviceType); + } + + @Override + public void onStartDiscoveryFailed(String serviceType, int errorCode) { + Log.e(TAG, "Discovery failed: Error code:" + errorCode); + mNsdManager.stopServiceDiscovery(this); + } + + @Override + public void onStopDiscoveryFailed(String serviceType, int errorCode) { + Log.e(TAG, "Discovery failed: Error code:" + errorCode); + mNsdManager.stopServiceDiscovery(this); + } + }; + } + + private View.OnClickListener nextClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.step1_auto: + System.out.println("auto pressed"); + //getProgressBar().setIndeterminate(true); + //setProgressBarIndeterminateVisibility(true); + //setProgressBarVisibility(true); + //setProgressBarIndeterminate(true); + setButtonState(false); + + mNsdManager = (NsdManager) getActivity().getSystemService(Context.NSD_SERVICE); + mNsdManager.discoverServices("_ledd._tcp", NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener); + + break; + case R.id.step1_manual: + System.out.println("manual pressed"); + setButtonState(false); + StepOneFragment stepOneFragment = StepOneFragment.newInstance(null); + ((AssistantActivity) getActivity()).switchFragment(stepOneFragment); + setButtonState(true); + break; + default: + break; + } + } + }; + + void setButtonState(boolean state) { + mAuto.setEnabled(state); + mManual.setEnabled(state); + } + + @Override + public void onPause() { + if (mNsdManager != null) { + tearDown(); + setButtonState(true); + } + + super.onPause(); + } + + @Override + public void onDestroy() { + if (mNsdManager != null) tearDown(); + super.onDestroy(); + } + + // NsdHelper's tearDown method + public void tearDown() { + mNsdManager.stopServiceDiscovery(mDiscoveryListener); + } +} diff --git a/app/src/main/java/com/idlegandalf/ledd/utils/ColorPicker.java b/app/src/main/java/com/idlegandalf/ledd/utils/ColorPicker.java new file mode 100644 index 0000000..52ebfd6 --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/utils/ColorPicker.java @@ -0,0 +1,352 @@ +/* + * Copyright 2013 Piotr Adamus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.idlegandalf.ledd.utils; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ComposeShader; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Paint.Join; +import android.graphics.Paint.Style; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.RadialGradient; +import android.graphics.RectF; +import android.graphics.Shader.TileMode; +import android.graphics.SweepGradient; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +public class ColorPicker extends View { + + /** + * Customizable display parameters (in percents) + */ + private final int paramOuterPadding = 2; // outer padding of the whole color picker view + private final int paramInnerPadding = 5; // distance between value slider wheel and inner color wheel + private final int paramValueSliderWidth = 10; // width of the value slider + private final int paramArrowPointerSize = 4; // size of the arrow pointer; set to 0 to hide the pointer + + private Paint colorWheelPaint; + private Paint valueSliderPaint; + + private Paint colorViewPaint; + + private Paint colorPointerPaint; + private RectF colorPointerCoords; + + private Paint valuePointerPaint; + private Paint valuePointerArrowPaint; + + private RectF outerWheelRect; + private RectF innerWheelRect; + + private Path colorViewPath; + private Path valueSliderPath; + private Path arrowPointerPath; + + private Bitmap colorWheelBitmap; + + private int valueSliderWidth; + private int innerPadding; + private int outerPadding; + + private int arrowPointerSize; + private int outerWheelRadius; + private int innerWheelRadius; + private int colorWheelRadius; + + private Matrix gradientRotationMatrix; + + /** Currently selected color */ + private float[] colorHSV = new float[] { 0f, 0f, 1f }; + + public ColorPicker(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + public ColorPicker(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public ColorPicker(Context context) { + super(context); + init(); + } + + private void init() { + + colorPointerPaint = new Paint(); + colorPointerPaint.setStyle(Style.STROKE); + colorPointerPaint.setStrokeWidth(2f); + colorPointerPaint.setARGB(128, 0, 0, 0); + + valuePointerPaint = new Paint(); + valuePointerPaint.setStyle(Style.STROKE); + valuePointerPaint.setStrokeWidth(2f); + + valuePointerArrowPaint = new Paint(); + + colorWheelPaint = new Paint(); + colorWheelPaint.setAntiAlias(true); + colorWheelPaint.setDither(true); + + valueSliderPaint = new Paint(); + valueSliderPaint.setAntiAlias(true); + valueSliderPaint.setDither(true); + + colorViewPaint = new Paint(); + colorViewPaint.setAntiAlias(true); + + colorViewPath = new Path(); + valueSliderPath = new Path(); + arrowPointerPath = new Path(); + + outerWheelRect = new RectF(); + innerWheelRect = new RectF(); + + colorPointerCoords = new RectF(); + + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + int size = Math.min(widthSize, heightSize); + setMeasuredDimension(size, size); + } + + @SuppressLint("DrawAllocation") + @Override + protected void onDraw(Canvas canvas) { + + int centerX = getWidth() / 2; + int centerY = getHeight() / 2; + + // drawing color wheel + + canvas.drawBitmap(colorWheelBitmap, centerX - colorWheelRadius, centerY - colorWheelRadius, null); + + // drawing color view + + colorViewPaint.setColor(Color.HSVToColor(colorHSV)); + canvas.drawPath(colorViewPath, colorViewPaint); + + // drawing value slider + + float[] hsv = new float[] { colorHSV[0], colorHSV[1], 1f }; + + SweepGradient sweepGradient = new SweepGradient(centerX, centerY, new int[] { Color.BLACK, Color.HSVToColor(hsv), Color.WHITE }, null); + sweepGradient.setLocalMatrix(gradientRotationMatrix); + valueSliderPaint.setShader(sweepGradient); + + canvas.drawPath(valueSliderPath, valueSliderPaint); + + // drawing color wheel pointer + + float hueAngle = (float) Math.toRadians(colorHSV[0]); + int colorPointX = (int) (-Math.cos(hueAngle) * colorHSV[1] * colorWheelRadius) + centerX; + int colorPointY = (int) (-Math.sin(hueAngle) * colorHSV[1] * colorWheelRadius) + centerY; + + float pointerRadius = 0.075f * colorWheelRadius; + int pointerX = (int) (colorPointX - pointerRadius / 2); + int pointerY = (int) (colorPointY - pointerRadius / 2); + + colorPointerCoords.set(pointerX, pointerY, pointerX + pointerRadius, pointerY + pointerRadius); + canvas.drawOval(colorPointerCoords, colorPointerPaint); + + // drawing value pointer + + valuePointerPaint.setColor(Color.HSVToColor(new float[] { 0f, 0f, 1f - colorHSV[2] })); + + double valueAngle = (colorHSV[2] - 0.5f) * Math.PI; + float valueAngleX = (float) Math.cos(valueAngle); + float valueAngleY = (float) Math.sin(valueAngle); + + canvas.drawLine(valueAngleX * innerWheelRadius + centerX, valueAngleY * innerWheelRadius + centerY, valueAngleX * outerWheelRadius + centerX, + valueAngleY * outerWheelRadius + centerY, valuePointerPaint); + + // drawing pointer arrow + + if (arrowPointerSize > 0) { + drawPointerArrow(canvas); + } + + } + + private void drawPointerArrow(Canvas canvas) { + + int centerX = getWidth() / 2; + int centerY = getHeight() / 2; + + double tipAngle = (colorHSV[2] - 0.5f) * Math.PI; + double leftAngle = tipAngle + Math.PI / 96; + double rightAngle = tipAngle - Math.PI / 96; + + double tipAngleX = Math.cos(tipAngle) * outerWheelRadius; + double tipAngleY = Math.sin(tipAngle) * outerWheelRadius; + double leftAngleX = Math.cos(leftAngle) * (outerWheelRadius + arrowPointerSize); + double leftAngleY = Math.sin(leftAngle) * (outerWheelRadius + arrowPointerSize); + double rightAngleX = Math.cos(rightAngle) * (outerWheelRadius + arrowPointerSize); + double rightAngleY = Math.sin(rightAngle) * (outerWheelRadius + arrowPointerSize); + + arrowPointerPath.reset(); + arrowPointerPath.moveTo((float) tipAngleX + centerX, (float) tipAngleY + centerY); + arrowPointerPath.lineTo((float) leftAngleX + centerX, (float) leftAngleY + centerY); + arrowPointerPath.lineTo((float) rightAngleX + centerX, (float) rightAngleY + centerY); + arrowPointerPath.lineTo((float) tipAngleX + centerX, (float) tipAngleY + centerY); + + valuePointerArrowPaint.setColor(Color.HSVToColor(colorHSV)); + valuePointerArrowPaint.setStyle(Style.FILL); + canvas.drawPath(arrowPointerPath, valuePointerArrowPaint); + + valuePointerArrowPaint.setStyle(Style.STROKE); + valuePointerArrowPaint.setStrokeJoin(Join.ROUND); + valuePointerArrowPaint.setColor(Color.BLACK); + canvas.drawPath(arrowPointerPath, valuePointerArrowPaint); + + } + + @Override + protected void onSizeChanged(int width, int height, int oldw, int oldh) { + + int centerX = width / 2; + int centerY = height / 2; + + innerPadding = (int) (paramInnerPadding * width / 100); + outerPadding = (int) (paramOuterPadding * width / 100); + arrowPointerSize = (int) (paramArrowPointerSize * width / 100); + valueSliderWidth = (int) (paramValueSliderWidth * width / 100); + + outerWheelRadius = width / 2 - outerPadding - arrowPointerSize; + innerWheelRadius = outerWheelRadius - valueSliderWidth; + colorWheelRadius = innerWheelRadius - innerPadding; + + outerWheelRect.set(centerX - outerWheelRadius, centerY - outerWheelRadius, centerX + outerWheelRadius, centerY + outerWheelRadius); + innerWheelRect.set(centerX - innerWheelRadius, centerY - innerWheelRadius, centerX + innerWheelRadius, centerY + innerWheelRadius); + + colorWheelBitmap = createColorWheelBitmap(colorWheelRadius * 2, colorWheelRadius * 2); + + gradientRotationMatrix = new Matrix(); + gradientRotationMatrix.preRotate(270, width / 2, height / 2); + + colorViewPath.arcTo(outerWheelRect, 270, -180); + colorViewPath.arcTo(innerWheelRect, 90, 180); + + valueSliderPath.arcTo(outerWheelRect, 270, 180); + valueSliderPath.arcTo(innerWheelRect, 90, -180); + + } + + private Bitmap createColorWheelBitmap(int width, int height) { + + Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); + + int colorCount = 12; + int colorAngleStep = 360 / 12; + int colors[] = new int[colorCount + 1]; + float hsv[] = new float[] { 0f, 1f, 1f }; + for (int i = 0; i < colors.length; i++) { + hsv[0] = (i * colorAngleStep + 180) % 360; + colors[i] = Color.HSVToColor(hsv); + } + colors[colorCount] = colors[0]; + + SweepGradient sweepGradient = new SweepGradient(width / 2, height / 2, colors, null); + RadialGradient radialGradient = new RadialGradient(width / 2, height / 2, colorWheelRadius, 0xFFFFFFFF, 0x00FFFFFF, TileMode.CLAMP); + ComposeShader composeShader = new ComposeShader(sweepGradient, radialGradient, PorterDuff.Mode.SRC_OVER); + + colorWheelPaint.setShader(composeShader); + + Canvas canvas = new Canvas(bitmap); + canvas.drawCircle(width / 2, height / 2, colorWheelRadius, colorWheelPaint); + + return bitmap; + + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + int action = event.getAction(); + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_MOVE: + + int x = (int) event.getX(); + int y = (int) event.getY(); + int cx = x - getWidth() / 2; + int cy = y - getHeight() / 2; + double d = Math.sqrt(cx * cx + cy * cy); + + if (d <= colorWheelRadius) { + + colorHSV[0] = (float) (Math.toDegrees(Math.atan2(cy, cx)) + 180f); + colorHSV[1] = Math.max(0f, Math.min(1f, (float) (d / colorWheelRadius))); + + invalidate(); + + } else if (x >= getWidth() / 2 && d >= innerWheelRadius) { + + colorHSV[2] = (float) Math.max(0, Math.min(1, Math.atan2(cy, cx) / Math.PI + 0.5f)); + + invalidate(); + } + + return true; + } + return super.onTouchEvent(event); + } + + public void setColor(int color) { + Color.colorToHSV(color, colorHSV); + } + + public int getColor() { + return Color.HSVToColor(colorHSV); + } + + @Override + protected Parcelable onSaveInstanceState() { + Bundle state = new Bundle(); + state.putFloatArray("color", colorHSV); + state.putParcelable("super", super.onSaveInstanceState()); + return state; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (state instanceof Bundle) { + Bundle bundle = (Bundle) state; + colorHSV = bundle.getFloatArray("color"); + super.onRestoreInstanceState(bundle.getParcelable("super")); + } else { + super.onRestoreInstanceState(state); + } + } + +} diff --git a/app/src/main/java/com/idlegandalf/ledd/utils/ColorPickerDialog.java b/app/src/main/java/com/idlegandalf/ledd/utils/ColorPickerDialog.java new file mode 100644 index 0000000..097d2f1 --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/utils/ColorPickerDialog.java @@ -0,0 +1,54 @@ +package com.idlegandalf.ledd.utils; + + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.widget.RelativeLayout; +import android.widget.RelativeLayout.LayoutParams; + +public class ColorPickerDialog extends AlertDialog { + + private ColorPicker colorPickerView; + private final OnColorSelectedListener onColorSelectedListener; + + public ColorPickerDialog(Context context, int initialColor, OnColorSelectedListener onColorSelectedListener) { + super(context); + + this.onColorSelectedListener = onColorSelectedListener; + + RelativeLayout relativeLayout = new RelativeLayout(context); + LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT); + + colorPickerView = new ColorPicker(context); + colorPickerView.setColor(initialColor); + + relativeLayout.addView(colorPickerView, layoutParams); + + setButton(BUTTON_POSITIVE, context.getString(android.R.string.ok), onClickListener); + setButton(BUTTON_NEGATIVE, context.getString(android.R.string.cancel), onClickListener); + + setView(relativeLayout); + + } + + private OnClickListener onClickListener = new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case BUTTON_POSITIVE: + int selectedColor = colorPickerView.getColor(); + onColorSelectedListener.onColorSelected(selectedColor); + break; + case BUTTON_NEGATIVE: + dialog.dismiss(); + break; + } + } + }; + + public interface OnColorSelectedListener { + public void onColorSelected(int color); + } + +} diff --git a/app/src/main/java/com/idlegandalf/ledd/utils/ColorPickerPreference.java b/app/src/main/java/com/idlegandalf/ledd/utils/ColorPickerPreference.java new file mode 100644 index 0000000..576b0c0 --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/utils/ColorPickerPreference.java @@ -0,0 +1,73 @@ +package com.idlegandalf.ledd.utils; + + +import android.app.AlertDialog.Builder; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.preference.DialogPreference; +import android.util.AttributeSet; +import android.view.View; +import android.widget.RelativeLayout; +import android.widget.RelativeLayout.LayoutParams; + +public class ColorPickerPreference extends DialogPreference { + + public static final int DEFAULT_COLOR = Color.WHITE; + + private int selectedColor; + private ColorPicker colorPickerView; + + public ColorPickerPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected View onCreateDialogView() { + + RelativeLayout relativeLayout = new RelativeLayout(getContext()); + LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT); + + colorPickerView = new ColorPicker(getContext()); + colorPickerView.setId(1); + + relativeLayout.addView(colorPickerView, layoutParams); + + return relativeLayout; + + } + + @Override + protected void onBindDialogView(View view) { + super.onBindDialogView(view); + colorPickerView.setColor(selectedColor); + } + + @Override + protected void onPrepareDialogBuilder(Builder builder) { + super.onPrepareDialogBuilder(builder); + builder.setTitle(null); // remove dialog title to get more space for color picker + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + if (positiveResult && shouldPersist()) { + if (callChangeListener(colorPickerView.getColor())) { + selectedColor = colorPickerView.getColor(); + persistInt(selectedColor); + } + } + } + + @Override + protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { + selectedColor = restoreValue ? getPersistedInt(DEFAULT_COLOR) : (Integer) defaultValue; + } + + @Override + protected Object onGetDefaultValue(TypedArray a, int index) { + return a.getInt(index, DEFAULT_COLOR); + } + +} diff --git a/app/src/main/java/com/idlegandalf/ledd/utils/MultiColorPicker.java b/app/src/main/java/com/idlegandalf/ledd/utils/MultiColorPicker.java new file mode 100644 index 0000000..b6a20fe --- /dev/null +++ b/app/src/main/java/com/idlegandalf/ledd/utils/MultiColorPicker.java @@ -0,0 +1,401 @@ +/* + * Copyright 2013 Piotr Adamus + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.idlegandalf.ledd.utils; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ComposeShader; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Paint.Join; +import android.graphics.Paint.Style; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.RadialGradient; +import android.graphics.RectF; +import android.graphics.Shader.TileMode; +import android.graphics.SweepGradient; +import android.os.Bundle; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +public class MultiColorPicker extends View { + + /** + * Customizable display parameters (in percents) + */ + private final int paramOuterPadding = 2; // outer padding of the whole color picker view + private final int paramInnerPadding = 5; // distance between value slider wheel and inner color wheel + private final int paramValueSliderWidth = 10; // width of the value slider + private final int paramArrowPointerSize = 4; // size of the arrow pointer; set to 0 to hide the pointer + + private final int paramColorCount = 5; + private final float paramHueSpreadAngle = 30f; // in degrees + + private Paint colorWheelPaint; + private Paint valueSliderPaint; + + private Paint colorViewPaint; + + private Paint colorPointerPaint; + private RectF colorPointerCoords; + + private Paint valuePointerPaint; + private Paint valuePointerArrowPaint; + + private RectF outerWheelRect; + private RectF innerWheelRect; + + private Path colorViewPath; + private Path valueSliderPath; + private Path arrowPointerPath; + + private Bitmap colorWheelBitmap; + + private int valueSliderWidth; + private int innerPadding; + private int outerPadding; + + private int arrowPointerSize; + private int outerWheelRadius; + private int innerWheelRadius; + private int colorWheelRadius; + + private Matrix gradientRotationMatrix; + + /** Currently selected color */ + private float[] colorHSV = new float[] { 0f, 0f, 1f }; + private float[] adjacentHue = new float[paramColorCount]; + + public MultiColorPicker(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + public MultiColorPicker(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public MultiColorPicker(Context context) { + super(context); + init(); + } + + private void init() { + + colorPointerPaint = new Paint(); + colorPointerPaint.setStyle(Style.STROKE); + colorPointerPaint.setStrokeWidth(2f); + colorPointerPaint.setARGB(128, 0, 0, 0); + + valuePointerPaint = new Paint(); + valuePointerPaint.setStyle(Style.STROKE); + valuePointerPaint.setStrokeWidth(2f); + + valuePointerArrowPaint = new Paint(); + + colorWheelPaint = new Paint(); + colorWheelPaint.setAntiAlias(true); + colorWheelPaint.setDither(true); + + valueSliderPaint = new Paint(); + valueSliderPaint.setAntiAlias(true); + valueSliderPaint.setDither(true); + + colorViewPaint = new Paint(); + colorViewPaint.setAntiAlias(true); + + colorViewPath = new Path(); + valueSliderPath = new Path(); + arrowPointerPath = new Path(); + + outerWheelRect = new RectF(); + innerWheelRect = new RectF(); + + colorPointerCoords = new RectF(); + + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthSize = MeasureSpec.getSize(widthMeasureSpec); + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + int size = Math.min(widthSize, heightSize); + setMeasuredDimension(size, size); + } + + @SuppressLint("DrawAllocation") + @Override + protected void onDraw(Canvas canvas) { + + int centerX = getWidth() / 2; + int centerY = getHeight() / 2; + + // drawing color wheel + + canvas.drawBitmap(colorWheelBitmap, centerX - colorWheelRadius, centerY - colorWheelRadius, null); + + // drawing color view + + int[] segmentColors = getColors(); + float sweepAngleStep = 180f / paramColorCount; + for (int i = 0; i < paramColorCount; i++) { + + colorViewPath.reset(); + colorViewPath.arcTo(outerWheelRect, 270 - i * sweepAngleStep, -sweepAngleStep); + colorViewPath.arcTo(innerWheelRect, 90 + (paramColorCount - i - 1) * sweepAngleStep, sweepAngleStep); + + colorViewPaint.setColor(segmentColors[i]); + + canvas.drawPath(colorViewPath, colorViewPaint); + + } + + // drawing value slider + + float[] hsv = new float[] { colorHSV[0], colorHSV[1], 1f }; + + SweepGradient sweepGradient = new SweepGradient(centerX, centerY, new int[] { Color.BLACK, Color.HSVToColor(hsv), Color.WHITE }, null); + sweepGradient.setLocalMatrix(gradientRotationMatrix); + valueSliderPaint.setShader(sweepGradient); + + canvas.drawPath(valueSliderPath, valueSliderPaint); + + // drawing color wheel pointer + + for (int i = 0; i < paramColorCount; i++) { + drawColorWheelPointer(canvas, (float) Math.toRadians(adjacentHue[i])); + } + + // drawing value pointer + + valuePointerPaint.setColor(Color.HSVToColor(new float[] { 0f, 0f, 1f - colorHSV[2] })); + + double valueAngle = (colorHSV[2] - 0.5f) * Math.PI; + float valueAngleX = (float) Math.cos(valueAngle); + float valueAngleY = (float) Math.sin(valueAngle); + + canvas.drawLine(valueAngleX * innerWheelRadius + centerX, valueAngleY * innerWheelRadius + centerY, valueAngleX * outerWheelRadius + centerX, + valueAngleY * outerWheelRadius + centerY, valuePointerPaint); + + // drawing pointer arrow + + if (arrowPointerSize > 0) { + drawPointerArrow(canvas); + } + + } + + private void drawColorWheelPointer(Canvas canvas, float hueAngle) { + + int centerX = getWidth() / 2; + int centerY = getHeight() / 2; + + int colorPointX = (int) (-Math.cos(hueAngle) * colorHSV[1] * colorWheelRadius) + centerX; + int colorPointY = (int) (-Math.sin(hueAngle) * colorHSV[1] * colorWheelRadius) + centerY; + + float pointerRadius = 0.075f * colorWheelRadius; + int pointerX = (int) (colorPointX - pointerRadius / 2); + int pointerY = (int) (colorPointY - pointerRadius / 2); + + colorPointerCoords.set(pointerX, pointerY, pointerX + pointerRadius, pointerY + pointerRadius); + canvas.drawOval(colorPointerCoords, colorPointerPaint); + + } + + private void drawPointerArrow(Canvas canvas) { + + int centerX = getWidth() / 2; + int centerY = getHeight() / 2; + + double tipAngle = (colorHSV[2] - 0.5f) * Math.PI; + double leftAngle = tipAngle + Math.PI / 96; + double rightAngle = tipAngle - Math.PI / 96; + + double tipAngleX = Math.cos(tipAngle) * outerWheelRadius; + double tipAngleY = Math.sin(tipAngle) * outerWheelRadius; + double leftAngleX = Math.cos(leftAngle) * (outerWheelRadius + arrowPointerSize); + double leftAngleY = Math.sin(leftAngle) * (outerWheelRadius + arrowPointerSize); + double rightAngleX = Math.cos(rightAngle) * (outerWheelRadius + arrowPointerSize); + double rightAngleY = Math.sin(rightAngle) * (outerWheelRadius + arrowPointerSize); + + arrowPointerPath.reset(); + arrowPointerPath.moveTo((float) tipAngleX + centerX, (float) tipAngleY + centerY); + arrowPointerPath.lineTo((float) leftAngleX + centerX, (float) leftAngleY + centerY); + arrowPointerPath.lineTo((float) rightAngleX + centerX, (float) rightAngleY + centerY); + arrowPointerPath.lineTo((float) tipAngleX + centerX, (float) tipAngleY + centerY); + + valuePointerArrowPaint.setColor(Color.HSVToColor(colorHSV)); + valuePointerArrowPaint.setStyle(Style.FILL); + canvas.drawPath(arrowPointerPath, valuePointerArrowPaint); + + valuePointerArrowPaint.setStyle(Style.STROKE); + valuePointerArrowPaint.setStrokeJoin(Join.ROUND); + valuePointerArrowPaint.setColor(Color.BLACK); + canvas.drawPath(arrowPointerPath, valuePointerArrowPaint); + + } + + @Override + protected void onSizeChanged(int width, int height, int oldw, int oldh) { + + int centerX = width / 2; + int centerY = height / 2; + + innerPadding = (int) (paramInnerPadding * width / 100); + outerPadding = (int) (paramOuterPadding * width / 100); + arrowPointerSize = (int) (paramArrowPointerSize * width / 100); + valueSliderWidth = (int) (paramValueSliderWidth * width / 100); + + outerWheelRadius = width / 2 - outerPadding - arrowPointerSize; + innerWheelRadius = outerWheelRadius - valueSliderWidth; + colorWheelRadius = innerWheelRadius - innerPadding; + + outerWheelRect.set(centerX - outerWheelRadius, centerY - outerWheelRadius, centerX + outerWheelRadius, centerY + outerWheelRadius); + innerWheelRect.set(centerX - innerWheelRadius, centerY - innerWheelRadius, centerX + innerWheelRadius, centerY + innerWheelRadius); + + colorWheelBitmap = createColorWheelBitmap(colorWheelRadius * 2, colorWheelRadius * 2); + + gradientRotationMatrix = new Matrix(); + gradientRotationMatrix.preRotate(270, width / 2, height / 2); + + valueSliderPath.arcTo(outerWheelRect, 270, 180); + valueSliderPath.arcTo(innerWheelRect, 90, -180); + + } + + private Bitmap createColorWheelBitmap(int width, int height) { + + Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); + + int colorCount = 12; + int colorAngleStep = 360 / 12; + int colors[] = new int[colorCount + 1]; + float hsv[] = new float[] { 0f, 1f, 1f }; + for (int i = 0; i < colors.length; i++) { + hsv[0] = (i * colorAngleStep + 180) % 360; + colors[i] = Color.HSVToColor(hsv); + } + colors[colorCount] = colors[0]; + + SweepGradient sweepGradient = new SweepGradient(width / 2, height / 2, colors, null); + RadialGradient radialGradient = new RadialGradient(width / 2, height / 2, colorWheelRadius, 0xFFFFFFFF, 0x00FFFFFF, TileMode.CLAMP); + ComposeShader composeShader = new ComposeShader(sweepGradient, radialGradient, PorterDuff.Mode.SRC_OVER); + + colorWheelPaint.setShader(composeShader); + + Canvas canvas = new Canvas(bitmap); + canvas.drawCircle(width / 2, height / 2, colorWheelRadius, colorWheelPaint); + + return bitmap; + + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + int action = event.getAction(); + switch (action) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_MOVE: + + int x = (int) event.getX(); + int y = (int) event.getY(); + int cx = x - getWidth() / 2; + int cy = y - getHeight() / 2; + double d = Math.sqrt(cx * cx + cy * cy); + + if (d <= colorWheelRadius) { + + colorHSV[0] = (float) (Math.toDegrees(Math.atan2(cy, cx)) + 180f); + colorHSV[1] = Math.max(0f, Math.min(1f, (float) (d / colorWheelRadius))); + + updateAdjacentHue(); + invalidate(); + + } else if (x >= getWidth() / 2 && d >= innerWheelRadius) { + + colorHSV[2] = (float) Math.max(0, Math.min(1, Math.atan2(cy, cx) / Math.PI + 0.5f)); + + updateAdjacentHue(); + invalidate(); + + } + + return true; + } + return super.onTouchEvent(event); + } + + private void updateAdjacentHue() { + + for (int i = 0; i < paramColorCount; i++) { + adjacentHue[i] = (colorHSV[0] - paramHueSpreadAngle * (paramColorCount / 2 - i)) % 360.0f; + adjacentHue[i] = (adjacentHue[i] < 0) ? adjacentHue[i] + 360f : adjacentHue[i]; + } + adjacentHue[paramColorCount / 2] = colorHSV[0]; + + } + + public void setColor(int color) { + Color.colorToHSV(color, colorHSV); + updateAdjacentHue(); + } + + public int getColor() { + return Color.HSVToColor(colorHSV); + } + + public int[] getColors() { + int[] colors = new int[paramColorCount]; + float[] hsv = new float[3]; + for (int i = 0; i < paramColorCount; i++) { + hsv[0] = adjacentHue[i]; + hsv[1] = colorHSV[1]; + hsv[2] = colorHSV[2]; + colors[i] = Color.HSVToColor(hsv); + } + return colors; + } + + @Override + protected Parcelable onSaveInstanceState() { + Bundle state = new Bundle(); + state.putFloatArray("color", colorHSV); + state.putParcelable("super", super.onSaveInstanceState()); + return state; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (state instanceof Bundle) { + Bundle bundle = (Bundle) state; + colorHSV = bundle.getFloatArray("color"); + updateAdjacentHue(); + super.onRestoreInstanceState(bundle.getParcelable("super")); + } else { + super.onRestoreInstanceState(state); + } + } + +} diff --git a/app/src/main/res/drawable-hdpi/drawer_shadow.9.png b/app/src/main/res/drawable-hdpi/drawer_shadow.9.png new file mode 100644 index 0000000..236bff5 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/drawer_shadow.9.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_settings.png b/app/src/main/res/drawable-hdpi/ic_action_settings.png new file mode 100644 index 0000000..0eb78f7 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_settings.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_drawer.png b/app/src/main/res/drawable-hdpi/ic_drawer.png new file mode 100644 index 0000000..c59f601 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_drawer.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_launcher.png b/app/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..36b4a5d Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-mdpi/drawer_shadow.9.png b/app/src/main/res/drawable-mdpi/drawer_shadow.9.png new file mode 100644 index 0000000..ffe3a28 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/drawer_shadow.9.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_settings.png b/app/src/main/res/drawable-mdpi/ic_action_settings.png new file mode 100644 index 0000000..c290e59 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_settings.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_drawer.png b/app/src/main/res/drawable-mdpi/ic_drawer.png new file mode 100644 index 0000000..1ed2c56 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_drawer.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_launcher.png b/app/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..9ce85cf Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-nodpi/ic_launcher.png b/app/src/main/res/drawable-nodpi/ic_launcher.png new file mode 100644 index 0000000..90c0f0b Binary files /dev/null and b/app/src/main/res/drawable-nodpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png b/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png new file mode 100644 index 0000000..fabe9d9 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/drawer_shadow.9.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_settings.png b/app/src/main/res/drawable-xhdpi/ic_action_settings.png new file mode 100644 index 0000000..999d0f0 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_settings.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_drawer.png b/app/src/main/res/drawable-xhdpi/ic_drawer.png new file mode 100644 index 0000000..a5fa74d Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_drawer.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher.png b/app/src/main/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..fca8186 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png b/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png new file mode 100644 index 0000000..b91e9d7 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/drawer_shadow.9.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_settings.png b/app/src/main/res/drawable-xxhdpi/ic_action_settings.png new file mode 100644 index 0000000..530227e Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_settings.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_drawer.png b/app/src/main/res/drawable-xxhdpi/ic_drawer.png new file mode 100644 index 0000000..9c4685d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_drawer.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..0ae19d6 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/drawable/dimme.xml b/app/src/main/res/drawable/dimme.xml new file mode 100644 index 0000000..dbc433c --- /dev/null +++ b/app/src/main/res/drawable/dimme.xml @@ -0,0 +1,6 @@ + + + + diff --git a/app/src/main/res/layout/act_demo.xml b/app/src/main/res/layout/act_demo.xml new file mode 100644 index 0000000..31ed633 --- /dev/null +++ b/app/src/main/res/layout/act_demo.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/act_multi_picker.xml b/app/src/main/res/layout/act_multi_picker.xml new file mode 100644 index 0000000..d064d56 --- /dev/null +++ b/app/src/main/res/layout/act_multi_picker.xml @@ -0,0 +1,21 @@ + + + + +