();
210 |
211 | for (int i = 0; i < getCount(); ++i) {
212 | result.add(mListMapping.get(i, i));
213 | }
214 |
215 | return result;
216 | }
217 |
218 | /**
219 | * Get the list position mapped to by the provided Cursor position. If the
220 | * provided Cursor position has been removed by a drag-sort, this returns
221 | * {@link #REMOVED}.
222 | *
223 | * @param cursorPosition
224 | * A Cursor position
225 | * @return The mapped-to list position or REMOVED
226 | */
227 | public int getListPosition(int cursorPosition) {
228 | if (mRemovedCursorPositions.contains(cursorPosition)) {
229 | return REMOVED;
230 | }
231 |
232 | int index = mListMapping.indexOfValue(cursorPosition);
233 | if (index < 0) {
234 | return cursorPosition;
235 | } else {
236 | return mListMapping.keyAt(index);
237 | }
238 | }
239 |
240 | }
241 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mobeta/android/dslv/DragSortItemView.java:
--------------------------------------------------------------------------------
1 | package com.mobeta.android.dslv;
2 |
3 | import android.content.Context;
4 | import android.view.Gravity;
5 | import android.view.View;
6 | import android.view.View.MeasureSpec;
7 | import android.view.ViewGroup;
8 | import android.widget.AbsListView;
9 |
10 | /**
11 | * Lightweight ViewGroup that wraps list items obtained from user's ListAdapter.
12 | * ItemView expects a single child that has a definite height (i.e. the child's
13 | * layout height is not MATCH_PARENT). The width of ItemView will always match
14 | * the width of its child (that is, the width MeasureSpec given to ItemView is
15 | * passed directly to the child, and the ItemView measured width is set to the
16 | * child's measured width). The height of ItemView can be anything; the
17 | *
18 | *
19 | * The purpose of this class is to optimize slide shuffle animations.
20 | */
21 | public class DragSortItemView extends ViewGroup {
22 |
23 | private int mGravity = Gravity.TOP;
24 |
25 | public DragSortItemView(Context context) {
26 | super(context);
27 |
28 | // always init with standard ListView layout params
29 | setLayoutParams(new AbsListView.LayoutParams(
30 | ViewGroup.LayoutParams.FILL_PARENT,
31 | ViewGroup.LayoutParams.WRAP_CONTENT));
32 |
33 | // setClipChildren(true);
34 | }
35 |
36 | public void setGravity(int gravity) {
37 | mGravity = gravity;
38 | }
39 |
40 | public int getGravity() {
41 | return mGravity;
42 | }
43 |
44 | @Override
45 | protected void onLayout(boolean changed, int left, int top, int right,
46 | int bottom) {
47 | final View child = getChildAt(0);
48 |
49 | if (child == null) {
50 | return;
51 | }
52 |
53 | if (mGravity == Gravity.TOP) {
54 | child.layout(0, 0, getMeasuredWidth(), child.getMeasuredHeight());
55 | } else {
56 | child.layout(0, getMeasuredHeight() - child.getMeasuredHeight(),
57 | getMeasuredWidth(), getMeasuredHeight());
58 | }
59 | }
60 |
61 | /**
62 | *
63 | */
64 | @Override
65 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
66 |
67 | int height = MeasureSpec.getSize(heightMeasureSpec);
68 | int width = MeasureSpec.getSize(widthMeasureSpec);
69 |
70 | int heightMode = MeasureSpec.getMode(heightMeasureSpec);
71 |
72 | final View child = getChildAt(0);
73 | if (child == null) {
74 | setMeasuredDimension(0, width);
75 | return;
76 | }
77 |
78 | if (child.isLayoutRequested()) {
79 | // Always let child be as tall as it wants.
80 | measureChild(child, widthMeasureSpec,
81 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
82 | }
83 |
84 | if (heightMode == MeasureSpec.UNSPECIFIED) {
85 | ViewGroup.LayoutParams lp = getLayoutParams();
86 |
87 | if (lp.height > 0) {
88 | height = lp.height;
89 | } else {
90 | height = child.getMeasuredHeight();
91 | }
92 | }
93 |
94 | setMeasuredDimension(width, height);
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mobeta/android/dslv/DragSortItemViewCheckable.java:
--------------------------------------------------------------------------------
1 | package com.mobeta.android.dslv;
2 |
3 | import android.content.Context;
4 | import android.view.Gravity;
5 | import android.view.View;
6 | import android.view.View.MeasureSpec;
7 | import android.view.ViewGroup;
8 | import android.widget.AbsListView;
9 | import android.widget.Checkable;
10 | import android.util.Log;
11 |
12 | /**
13 | * Lightweight ViewGroup that wraps list items obtained from user's ListAdapter.
14 | * ItemView expects a single child that has a definite height (i.e. the child's
15 | * layout height is not MATCH_PARENT). The width of ItemView will always match
16 | * the width of its child (that is, the width MeasureSpec given to ItemView is
17 | * passed directly to the child, and the ItemView measured width is set to the
18 | * child's measured width). The height of ItemView can be anything; the
19 | *
20 | *
21 | * The purpose of this class is to optimize slide shuffle animations.
22 | */
23 | public class DragSortItemViewCheckable extends DragSortItemView implements
24 | Checkable {
25 |
26 | public DragSortItemViewCheckable(Context context) {
27 | super(context);
28 | }
29 |
30 | @Override
31 | public boolean isChecked() {
32 | View child = getChildAt(0);
33 | if (child instanceof Checkable)
34 | return ((Checkable) child).isChecked();
35 | else
36 | return false;
37 | }
38 |
39 | @Override
40 | public void setChecked(boolean checked) {
41 | View child = getChildAt(0);
42 | if (child instanceof Checkable)
43 | ((Checkable) child).setChecked(checked);
44 | }
45 |
46 | @Override
47 | public void toggle() {
48 | View child = getChildAt(0);
49 | if (child instanceof Checkable)
50 | ((Checkable) child).toggle();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mobeta/android/dslv/ResourceDragSortCursorAdapter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.mobeta.android.dslv;
18 |
19 | import android.content.Context;
20 | import android.database.Cursor;
21 | import android.view.View;
22 | import android.view.ViewGroup;
23 | import android.view.LayoutInflater;
24 |
25 | // taken from v4 rev. 10 ResourceCursorAdapter.java
26 |
27 | /**
28 | * Static library support version of the framework's
29 | * {@link android.widget.ResourceCursorAdapter}. Used to write apps that run on
30 | * platforms prior to Android 3.0. When running on Android 3.0 or above, this
31 | * implementation is still used; it does not try to switch to the framework's
32 | * implementation. See the framework SDK documentation for a class overview.
33 | */
34 | public abstract class ResourceDragSortCursorAdapter extends
35 | DragSortCursorAdapter {
36 | private int mLayout;
37 |
38 | private int mDropDownLayout;
39 |
40 | private LayoutInflater mInflater;
41 |
42 | /**
43 | * Constructor the enables auto-requery.
44 | *
45 | * @deprecated This option is discouraged, as it results in Cursor queries
46 | * being performed on the application's UI thread and thus can
47 | * cause poor responsiveness or even Application Not Responding
48 | * errors. As an alternative, use
49 | * {@link android.app.LoaderManager} with a
50 | * {@link android.content.CursorLoader}.
51 | *
52 | * @param context
53 | * The context where the ListView associated with this adapter is
54 | * running
55 | * @param layout
56 | * resource identifier of a layout file that defines the views
57 | * for this list item. Unless you override them later, this will
58 | * define both the item views and the drop down views.
59 | */
60 | @Deprecated
61 | public ResourceDragSortCursorAdapter(Context context, int layout, Cursor c) {
62 | super(context, c);
63 | mLayout = mDropDownLayout = layout;
64 | mInflater = (LayoutInflater) context
65 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
66 | }
67 |
68 | /**
69 | * Constructor with default behavior as per
70 | * {@link CursorAdapter#CursorAdapter(Context, Cursor, boolean)}; it is
71 | * recommended you not use this, but instead
72 | * {@link #ResourceCursorAdapter(Context, int, Cursor, int)}. When using
73 | * this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER} will always be
74 | * set.
75 | *
76 | * @param context
77 | * The context where the ListView associated with this adapter is
78 | * running
79 | * @param layout
80 | * resource identifier of a layout file that defines the views
81 | * for this list item. Unless you override them later, this will
82 | * define both the item views and the drop down views.
83 | * @param c
84 | * The cursor from which to get the data.
85 | * @param autoRequery
86 | * If true the adapter will call requery() on the cursor whenever
87 | * it changes so the most recent data is always displayed. Using
88 | * true here is discouraged.
89 | */
90 | public ResourceDragSortCursorAdapter(Context context, int layout, Cursor c,
91 | boolean autoRequery) {
92 | super(context, c, autoRequery);
93 | mLayout = mDropDownLayout = layout;
94 | mInflater = (LayoutInflater) context
95 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
96 | }
97 |
98 | /**
99 | * Standard constructor.
100 | *
101 | * @param context
102 | * The context where the ListView associated with this adapter is
103 | * running
104 | * @param layout
105 | * Resource identifier of a layout file that defines the views
106 | * for this list item. Unless you override them later, this will
107 | * define both the item views and the drop down views.
108 | * @param c
109 | * The cursor from which to get the data.
110 | * @param flags
111 | * Flags used to determine the behavior of the adapter, as per
112 | * {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
113 | */
114 | public ResourceDragSortCursorAdapter(Context context, int layout, Cursor c,
115 | int flags) {
116 | super(context, c, flags);
117 | mLayout = mDropDownLayout = layout;
118 | mInflater = (LayoutInflater) context
119 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
120 | }
121 |
122 | /**
123 | * Inflates view(s) from the specified XML file.
124 | *
125 | * @see android.widget.CursorAdapter#newView(android.content.Context,
126 | * android.database.Cursor, ViewGroup)
127 | */
128 | @Override
129 | public View newView(Context context, Cursor cursor, ViewGroup parent) {
130 | return mInflater.inflate(mLayout, parent, false);
131 | }
132 |
133 | @Override
134 | public View newDropDownView(Context context, Cursor cursor, ViewGroup parent) {
135 | return mInflater.inflate(mDropDownLayout, parent, false);
136 | }
137 |
138 | /**
139 | *
140 | * Sets the layout resource of the item views.
141 | *
142 | *
143 | * @param layout
144 | * the layout resources used to create item views
145 | */
146 | public void setViewResource(int layout) {
147 | mLayout = layout;
148 | }
149 |
150 | /**
151 | *
152 | * Sets the layout resource of the drop down views.
153 | *
154 | *
155 | * @param dropDownLayout
156 | * the layout resources used to create drop down views
157 | */
158 | public void setDropDownViewResource(int dropDownLayout) {
159 | mDropDownLayout = dropDownLayout;
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/app/src/main/java/com/mobeta/android/dslv/SimpleFloatViewManager.java:
--------------------------------------------------------------------------------
1 | package com.mobeta.android.dslv;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Point;
5 | import android.graphics.Color;
6 | import android.widget.ListView;
7 | import android.widget.ImageView;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 |
11 | /**
12 | * Simple implementation of the FloatViewManager class. Uses list items as they
13 | * appear in the ListView to create the floating View.
14 | */
15 | public class SimpleFloatViewManager implements
16 | DragSortListView.FloatViewManager {
17 |
18 | private Bitmap mFloatBitmap;
19 |
20 | private ImageView mImageView;
21 |
22 | private int mFloatBGColor = Color.BLACK;
23 |
24 | private ListView mListView;
25 |
26 | public SimpleFloatViewManager(ListView lv) {
27 | mListView = lv;
28 | }
29 |
30 | public void setBackgroundColor(int color) {
31 | mFloatBGColor = color;
32 | }
33 |
34 | /**
35 | * This simple implementation creates a Bitmap copy of the list item
36 | * currently shown at ListView position
.
37 | */
38 | @Override
39 | public View onCreateFloatView(int position) {
40 | // Guaranteed that this will not be null? I think so. Nope, got
41 | // a NullPointerException once...
42 | View v = mListView.getChildAt(position
43 | + mListView.getHeaderViewsCount()
44 | - mListView.getFirstVisiblePosition());
45 |
46 | if (v == null) {
47 | return null;
48 | }
49 |
50 | v.setPressed(false);
51 |
52 | // Create a copy of the drawing cache so that it does not get
53 | // recycled by the framework when the list tries to clean up memory
54 | // v.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
55 | v.setDrawingCacheEnabled(true);
56 | mFloatBitmap = Bitmap.createBitmap(v.getDrawingCache());
57 | v.setDrawingCacheEnabled(false);
58 |
59 | if (mImageView == null) {
60 | mImageView = new ImageView(mListView.getContext());
61 | }
62 | mImageView.setBackgroundColor(mFloatBGColor);
63 | mImageView.setPadding(0, 0, 0, 0);
64 | mImageView.setImageBitmap(mFloatBitmap);
65 | mImageView.setLayoutParams(new ViewGroup.LayoutParams(v.getWidth(), v
66 | .getHeight()));
67 |
68 | return mImageView;
69 | }
70 |
71 | /**
72 | * This does nothing
73 | */
74 | @Override
75 | public void onDragFloatView(View floatView, Point position, Point touch) {
76 | // do nothing
77 | }
78 |
79 | /**
80 | * Removes the Bitmap from the ImageView created in onCreateFloatView() and
81 | * tells the system to recycle it.
82 | */
83 | @Override
84 | public void onDestroyFloatView(View floatView) {
85 | ((ImageView) floatView).setImageDrawable(null);
86 |
87 | mFloatBitmap.recycle();
88 | mFloatBitmap = null;
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/AboutFragment.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2010 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.content.Context;
21 | import android.content.pm.PackageInfo;
22 | import android.content.pm.PackageManager.NameNotFoundException;
23 | import android.os.Bundle;
24 | import android.view.LayoutInflater;
25 | import android.view.View;
26 | import android.view.ViewGroup;
27 | import android.widget.TextView;
28 |
29 | import androidx.fragment.app.Fragment;
30 |
31 | public class AboutFragment extends Fragment {
32 | @Override
33 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
34 | Bundle savedInstanceState) {
35 | View v = inflater.inflate(R.layout.about_fragment, container, false);
36 |
37 | PackageInfo pinfo;
38 | try {
39 | Context context = getActivity();
40 | pinfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
41 | } catch (NameNotFoundException e) {
42 | throw new RuntimeException("Can't find package?");
43 | }
44 |
45 | // Evaluate the format string in VersionText.
46 | TextView versionText = (TextView) v.findViewById(R.id.VersionText);
47 | String versionFormat = versionText.getText().toString();
48 | versionText.setText(String.format(versionFormat, pinfo.versionName));
49 |
50 | return v;
51 | }
52 |
53 | @Override
54 | public void onResume() {
55 | super.onResume();
56 | ((ChromaDoze) getActivity()).setFragmentId(FragmentIndex.ID_ABOUT);
57 | }
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/AudioFocusHelper.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2014 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.content.ComponentName;
21 | import android.content.Context;
22 | import android.media.AudioFocusRequest;
23 | import android.media.AudioManager;
24 | import android.media.AudioManager.OnAudioFocusChangeListener;
25 | import android.os.Build;
26 |
27 | // This file keeps track of AudioFocus events.
28 | // http://developer.android.com/training/managing-audio/audio-focus.html
29 |
30 | class AudioFocusHelper implements OnAudioFocusChangeListener {
31 | private final Context mContext;
32 | private final SampleShuffler.VolumeListener mVolumeListener;
33 | private final AudioManager mAudioManager;
34 | private boolean mActive = false;
35 | private AudioFocusRequest mRequest;
36 |
37 | public AudioFocusHelper(Context ctx, SampleShuffler.VolumeListener volumeListener) {
38 | mContext = ctx;
39 | mVolumeListener = volumeListener;
40 | mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
41 |
42 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
43 | // For Android Oreo (API 26) and above
44 | mRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
45 | .setAudioAttributes(AudioParams.makeAudioAttributes())
46 | .setOnAudioFocusChangeListener(this)
47 | .build();
48 | }
49 | }
50 |
51 | public void setActive(boolean active) {
52 | if (mActive == active) {
53 | return;
54 | }
55 | if (active) {
56 | requestFocus();
57 | } else {
58 | abandonFocus();
59 | }
60 | mActive = active;
61 | }
62 |
63 | @SuppressWarnings("deprecation")
64 | private void requestFocus() {
65 | // I'm too lazy to check the return value.
66 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
67 | mAudioManager.requestAudioFocus(mRequest);
68 | } else {
69 | mAudioManager.requestAudioFocus(this, AudioParams.STREAM_TYPE, AudioManager.AUDIOFOCUS_GAIN);
70 | }
71 | }
72 |
73 | @SuppressWarnings("deprecation")
74 | private void abandonFocus() {
75 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
76 | mAudioManager.abandonAudioFocusRequest(mRequest);
77 | } else {
78 | mAudioManager.abandonAudioFocus(this);
79 | }
80 | }
81 |
82 | @Override
83 | public void onAudioFocusChange(int focusChange) {
84 | switch (focusChange) {
85 | case AudioManager.AUDIOFOCUS_LOSS:
86 | // For example, a music player or a sleep timer stealing focus.
87 | NoiseService.stopNow(mContext, R.string.stop_reason_audiofocus);
88 | break;
89 | case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
90 | // For example, an alarm or phone call.
91 | mVolumeListener.setDuckLevel(SampleShuffler.VolumeListener.DuckLevel.SILENT);
92 | break;
93 | case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
94 | // For example, an email notification.
95 | mVolumeListener.setDuckLevel(SampleShuffler.VolumeListener.DuckLevel.DUCK);
96 | break;
97 | case AudioManager.AUDIOFOCUS_GAIN:
98 | // Resume the default volume level.
99 | mVolumeListener.setDuckLevel(SampleShuffler.VolumeListener.DuckLevel.NORMAL);
100 | break;
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/AudioParams.java:
--------------------------------------------------------------------------------
1 | package net.pmarks.chromadoze;
2 |
3 | import android.media.AudioAttributes;
4 | import android.media.AudioFormat;
5 | import android.media.AudioManager;
6 | import android.media.AudioTrack;
7 | import android.os.Build;
8 |
9 | import androidx.annotation.RequiresApi;
10 |
11 | class AudioParams {
12 | final static int STREAM_TYPE = AudioManager.STREAM_MUSIC;
13 | final static int CHANNEL_CONFIG = AudioFormat.CHANNEL_OUT_STEREO;
14 | final static int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
15 | final static int SHORTS_PER_SAMPLE = 2; // 16-bit Stereo
16 | final static int BYTES_PER_SAMPLE = 4; // 16-bit Stereo
17 | final static int LATENCY_MS = 100;
18 | final int SAMPLE_RATE;
19 | final int BUF_BYTES;
20 | final int BUF_SAMPLES;
21 |
22 | AudioParams() {
23 | SAMPLE_RATE = AudioTrack.getNativeOutputSampleRate(STREAM_TYPE);
24 | BUF_BYTES = Math.max(
25 | AudioTrack.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT),
26 | (SAMPLE_RATE * LATENCY_MS / 1000) * BYTES_PER_SAMPLE);
27 | BUF_SAMPLES = BUF_BYTES / BYTES_PER_SAMPLE;
28 | }
29 |
30 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
31 | static AudioAttributes makeAudioAttributes() {
32 | return new AudioAttributes.Builder()
33 | .setUsage(AudioAttributes.USAGE_MEDIA)
34 | .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
35 | .build();
36 | }
37 |
38 | @SuppressWarnings("deprecation")
39 | private AudioTrack makeAudioTrackLegacy() {
40 | return new AudioTrack(
41 | STREAM_TYPE, SAMPLE_RATE, CHANNEL_CONFIG,
42 | AUDIO_FORMAT, BUF_BYTES, AudioTrack.MODE_STREAM);
43 | }
44 |
45 | AudioTrack makeAudioTrack() {
46 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
47 | return new AudioTrack(makeAudioAttributes(),
48 | new AudioFormat.Builder()
49 | .setSampleRate(SAMPLE_RATE)
50 | .setChannelMask(CHANNEL_CONFIG)
51 | .setEncoding(AUDIO_FORMAT)
52 | .build(),
53 | BUF_BYTES,
54 | AudioTrack.MODE_STREAM,
55 | AudioManager.AUDIO_SESSION_ID_GENERATE);
56 | } else {
57 | return makeAudioTrackLegacy();
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/CheckableLinearLayout.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.content.Context;
21 | import android.util.AttributeSet;
22 | import android.widget.Checkable;
23 | import android.widget.LinearLayout;
24 |
25 | public class CheckableLinearLayout extends LinearLayout implements Checkable {
26 |
27 | private Checkable mChild;
28 |
29 | public CheckableLinearLayout(Context context, AttributeSet attrs) {
30 | super(context, attrs);
31 | }
32 |
33 | @Override
34 | protected void onFinishInflate() {
35 | super.onFinishInflate();
36 | for (int i = 0; i < getChildCount(); i++) {
37 | try {
38 | mChild = (Checkable) getChildAt(i);
39 | return;
40 | } catch (ClassCastException e) {
41 | }
42 | }
43 | }
44 |
45 | @Override
46 | public boolean isChecked() {
47 | return mChild.isChecked();
48 | }
49 |
50 | @Override
51 | public void setChecked(boolean checked) {
52 | mChild.setChecked(checked);
53 | }
54 |
55 | @Override
56 | public void toggle() {
57 | mChild.toggle();
58 | }
59 |
60 | @Override
61 | public void setEnabled(boolean enabled) {
62 | super.setEnabled(enabled);
63 | for (int i = 0; i < getChildCount(); i++) {
64 | getChildAt(i).setEnabled(enabled);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/ChromaDoze.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2010 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.app.backup.BackupManager;
21 | import android.content.SharedPreferences;
22 | import android.graphics.Bitmap;
23 | import android.graphics.BlendMode;
24 | import android.graphics.BlendModeColorFilter;
25 | import android.graphics.PorterDuff;
26 | import android.graphics.PorterDuff.Mode;
27 | import android.graphics.drawable.BitmapDrawable;
28 | import android.graphics.drawable.Drawable;
29 | import android.os.Build;
30 | import android.os.Bundle;
31 | import android.util.TypedValue;
32 | import android.view.Menu;
33 | import android.view.MenuItem;
34 | import android.view.View;
35 | import android.view.ViewGroup;
36 | import android.widget.AdapterView;
37 | import android.widget.AdapterView.OnItemSelectedListener;
38 | import android.widget.ArrayAdapter;
39 | import android.widget.ImageButton;
40 | import android.widget.Spinner;
41 |
42 | import androidx.appcompat.app.ActionBar;
43 | import androidx.appcompat.app.AppCompatActivity;
44 | import androidx.appcompat.widget.Toolbar;
45 | import androidx.core.content.ContextCompat;
46 | import androidx.core.view.MenuItemCompat;
47 | import androidx.fragment.app.Fragment;
48 | import androidx.fragment.app.FragmentManager;
49 | import androidx.fragment.app.FragmentTransaction;
50 |
51 | import java.util.Date;
52 |
53 | public class ChromaDoze extends AppCompatActivity implements
54 | NoiseService.PercentListener, UIState.LockListener, OnItemSelectedListener {
55 | private static final int MENU_PLAY_STOP = 1;
56 | private static final int MENU_LOCK = 2;
57 |
58 | private UIState mUiState;
59 | private int mFragmentId = FragmentIndex.ID_CHROMA_DOZE;
60 |
61 | private Drawable mToolbarIcon;
62 | private Spinner mNavSpinner;
63 |
64 | private boolean mServiceActive;
65 |
66 | // The name to use when accessing our SharedPreferences.
67 | public static final String PREF_NAME = "ChromaDoze";
68 |
69 | @Override
70 | public void onCreate(Bundle savedInstanceState) {
71 | super.onCreate(savedInstanceState);
72 | setContentView(R.layout.main);
73 |
74 | mUiState = new UIState(getApplication());
75 |
76 | SharedPreferences pref = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
77 | mUiState.loadState(pref);
78 |
79 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
80 | setSupportActionBar(toolbar);
81 |
82 | ActionBar actionBar = getSupportActionBar();
83 | actionBar.setDisplayHomeAsUpEnabled(true);
84 | actionBar.setTitle("");
85 |
86 | mNavSpinner = (Spinner) findViewById(R.id.nav_spinner);
87 | ArrayAdapter adapter = new ArrayAdapter<>(
88 | actionBar.getThemedContext(), R.layout.spinner_title,
89 | FragmentIndex.getStrings(this));
90 | adapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item);
91 | mNavSpinner.setAdapter(adapter);
92 | mNavSpinner.setOnItemSelectedListener(this);
93 |
94 |
95 | // Created a scaled-down icon for the Toolbar.
96 | {
97 | TypedValue tv = new TypedValue();
98 | getTheme().resolveAttribute(R.attr.actionBarSize, tv, true);
99 | int height = TypedValue.complexToDimensionPixelSize(tv.data, getResources().getDisplayMetrics());
100 | // This originally used a scaled-down launcher icon, but I don't feel like figuring
101 | // out how to render R.mipmap.chromadoze_icon correctly.
102 | mToolbarIcon = ContextCompat.getDrawable(this, R.drawable.toolbar_icon);
103 | }
104 |
105 | // When this Activity is first created, set up the initial fragment.
106 | // After a save/restore, the framework will drop in the last-used
107 | // fragment automatically.
108 | if (savedInstanceState == null) {
109 | changeFragment(new MainFragment(), false);
110 | }
111 | }
112 |
113 | @Override
114 | public void onResume() {
115 | super.onResume();
116 | // Start receiving progress events.
117 | NoiseService.addPercentListener(this);
118 | mUiState.addLockListener(this);
119 |
120 | if (mUiState.getAutoPlay()) {
121 | mUiState.sendToService();
122 | }
123 | }
124 |
125 | @Override
126 | protected void onPause() {
127 | super.onPause();
128 |
129 | // If the equalizer is silent, stop the service.
130 | // This makes it harder to leave running accidentally.
131 | if (mServiceActive && mUiState.getPhonon().isSilent()) {
132 | NoiseService.stopNow(getApplication(), R.string.stop_reason_silent);
133 | }
134 |
135 | SharedPreferences.Editor pref = getSharedPreferences(PREF_NAME, MODE_PRIVATE).edit();
136 | pref.clear();
137 | mUiState.saveState(pref);
138 | pref.commit();
139 | new BackupManager(this).dataChanged();
140 |
141 | // Stop receiving progress events.
142 | NoiseService.removePercentListener(this);
143 | mUiState.removeLockListener(this);
144 | }
145 |
146 | @Override
147 | public boolean onCreateOptionsMenu(Menu menu) {
148 | menu.add(0, MENU_PLAY_STOP, 0, getString(R.string.play_stop)).setShowAsAction(
149 | MenuItem.SHOW_AS_ACTION_ALWAYS);
150 |
151 | if (mFragmentId == FragmentIndex.ID_CHROMA_DOZE) {
152 | menu.add(0, MENU_LOCK, 0, getString(R.string.lock_unlock)).setShowAsAction(
153 | MenuItem.SHOW_AS_ACTION_ALWAYS);
154 | }
155 |
156 | return super.onCreateOptionsMenu(menu);
157 | }
158 |
159 | @Override
160 | public boolean onPrepareOptionsMenu(Menu menu) {
161 | menu.findItem(MENU_PLAY_STOP).setIcon(
162 | mServiceActive ? R.drawable.av_stop : R.drawable.av_play);
163 | MenuItem mi = menu.findItem(MENU_LOCK);
164 | if (mi != null) {
165 | mi.setIcon(getLockIcon());
166 | }
167 | return super.onPrepareOptionsMenu(menu);
168 | }
169 |
170 | @Override
171 | public void onLockStateChange(LockEvent e) {
172 | // Redraw the lock icon for both event types.
173 | supportInvalidateOptionsMenu();
174 | }
175 |
176 | @SuppressWarnings("deprecation")
177 | private static void setColorFilterCompat(Drawable drawable, int color, PorterDuff.Mode mode) {
178 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
179 | drawable.setColorFilter(new BlendModeColorFilter(color, BlendMode.valueOf(mode.name())));
180 | } else {
181 | drawable.setColorFilter(color, mode);
182 | }
183 | }
184 |
185 | // Get the lock icon which reflects the current action.
186 | private Drawable getLockIcon() {
187 | Drawable d = ContextCompat.getDrawable(this, mUiState.getLocked() ?
188 | R.drawable.action_unlock : R.drawable.action_lock);
189 | if (mUiState.getLockBusy()) {
190 | setColorFilterCompat(d, 0xFFFF4444, Mode.SRC_IN);
191 | } else {
192 | d.clearColorFilter();
193 | }
194 | return d;
195 | }
196 |
197 | @Override
198 | public boolean onSupportNavigateUp() {
199 | // Rewind the back stack.
200 | getSupportFragmentManager().popBackStack(null,
201 | FragmentManager.POP_BACK_STACK_INCLUSIVE);
202 | return true;
203 | }
204 |
205 | @Override
206 | public boolean onOptionsItemSelected(MenuItem item) {
207 | switch (item.getItemId()) {
208 | case MENU_PLAY_STOP:
209 | // Force the service into its expected state.
210 | if (!mServiceActive) {
211 | mUiState.sendToService();
212 | } else {
213 | NoiseService.stopNow(getApplication(), R.string.stop_reason_toolbar);
214 | }
215 | return true;
216 | case MENU_LOCK:
217 | mUiState.toggleLocked();
218 | supportInvalidateOptionsMenu();
219 | return true;
220 | }
221 | return false;
222 | }
223 |
224 | @Override
225 | public void onNoiseServicePercentChange(int percent, Date stopTimestamp, int stopReasonId) {
226 | boolean newServiceActive = (percent >= 0);
227 | if (mServiceActive != newServiceActive) {
228 | mServiceActive = newServiceActive;
229 |
230 | // Redraw the "Play/Stop" button.
231 | supportInvalidateOptionsMenu();
232 | }
233 | }
234 |
235 | private void changeFragment(Fragment f, boolean allowBack) {
236 | FragmentManager fragmentManager = getSupportFragmentManager();
237 |
238 | // Prune the stack, so "back" always leads home.
239 | if (fragmentManager.getBackStackEntryCount() > 0) {
240 | onSupportNavigateUp();
241 | }
242 |
243 | FragmentTransaction transaction = fragmentManager.beginTransaction();
244 | transaction.replace(R.id.fragment_container, f);
245 | if (allowBack) {
246 | transaction.addToBackStack(null);
247 | transaction
248 | .setTransition(FragmentTransaction.TRANSIT_NONE);
249 | }
250 | transaction.commit();
251 | }
252 |
253 | // Fragments can read this >= onActivityCreated().
254 | public UIState getUIState() {
255 | return mUiState;
256 | }
257 |
258 | // Each fragment calls this from onResume to tweak the ActionBar.
259 | public void setFragmentId(int id) {
260 | mFragmentId = id;
261 |
262 | final boolean enableUp = id != FragmentIndex.ID_CHROMA_DOZE;
263 | ActionBar actionBar = getSupportActionBar();
264 | supportInvalidateOptionsMenu();
265 |
266 | // Use the default left arrow, or a scaled-down Chroma Doze icon.
267 | actionBar.setHomeAsUpIndicator(enableUp ? null : mToolbarIcon);
268 |
269 | // When we're on the main page, make the icon non-clickable.
270 | ImageButton navUp = findImageButton(findViewById(R.id.toolbar));
271 | if (navUp != null) {
272 | navUp.setClickable(enableUp);
273 | }
274 |
275 | mNavSpinner.setSelection(id);
276 | }
277 |
278 | // Search a View for the first ImageButton. We use it to locate the
279 | // home/up button in a Toolbar.
280 | private static ImageButton findImageButton(View view) {
281 | if (view instanceof ImageButton) {
282 | return (ImageButton) view;
283 | } else if (view instanceof ViewGroup) {
284 | ViewGroup vg = (ViewGroup) view;
285 | for (int i = 0; i < vg.getChildCount(); i++) {
286 | ImageButton found = findImageButton(vg.getChildAt(i));
287 | if (found != null) {
288 | return found;
289 | }
290 | }
291 | }
292 | return null;
293 | }
294 |
295 | // Handle nav_spinner selection.
296 | @Override
297 | public void onItemSelected(AdapterView> parent, View view, int position,
298 | long id) {
299 | if (position == mFragmentId) {
300 | return;
301 | }
302 | switch (position) {
303 | case FragmentIndex.ID_CHROMA_DOZE:
304 | onSupportNavigateUp();
305 | return;
306 | case FragmentIndex.ID_OPTIONS:
307 | changeFragment(new OptionsFragment(), true);
308 | return;
309 | case FragmentIndex.ID_MEMORY:
310 | changeFragment(new MemoryFragment(), true);
311 | return;
312 | case FragmentIndex.ID_ABOUT:
313 | changeFragment(new AboutFragment(), true);
314 | return;
315 | }
316 | }
317 |
318 | @Override
319 | public void onNothingSelected(AdapterView> parent) {
320 | }
321 | }
322 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/EqualizerView.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2010 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.content.Context;
21 | import android.graphics.Bitmap;
22 | import android.graphics.BitmapFactory;
23 | import android.graphics.Canvas;
24 | import android.graphics.Color;
25 | import android.graphics.Paint;
26 | import android.graphics.Path;
27 | import android.os.Build;
28 | import android.util.AttributeSet;
29 | import android.view.MotionEvent;
30 |
31 | import androidx.annotation.NonNull;
32 |
33 | public class EqualizerView extends android.view.View implements UIState.LockListener {
34 | private static final int BAND_COUNT = SpectrumData.BAND_COUNT;
35 |
36 | // 3D projection offsets (multiple of mBarWidth)
37 | private static final float PROJECT_X = 0.4f;
38 | private static final float PROJECT_Y = -0.25f;
39 |
40 | // L=light, M=medium, D=dark
41 | private final Paint mBarColorL[] = new Paint[BAND_COUNT];
42 | private final Paint mBarColorM[] = new Paint[BAND_COUNT];
43 | private final Paint mBarColorD[] = new Paint[BAND_COUNT];
44 | private final Paint mBaseColorL[] = new Paint[4];
45 | private final Paint mBaseColorM[] = new Paint[4];
46 | private final Paint mBaseColorD[] = new Paint[4];
47 |
48 | private UIState mUiState;
49 |
50 | private float mWidth;
51 | private float mHeight;
52 | private float mBarWidth;
53 | private float mZeroLineY;
54 |
55 | private Path mCubeTop;
56 | private Path mCubeSide;
57 |
58 | public EqualizerView(Context context, AttributeSet attrs) {
59 | super(context, attrs);
60 | makeColors();
61 | }
62 |
63 | public void setUiState(UIState uiState) {
64 | mUiState = uiState;
65 | invalidate();
66 | }
67 |
68 | private void makeColors() {
69 | Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.spectrum);
70 | for (int i = 0; i < BAND_COUNT; i++) {
71 | Paint p = new Paint();
72 | int x = (bmp.getWidth() - 1) * i / (BAND_COUNT - 1);
73 | p.setColor(bmp.getPixel(x, 0));
74 | mBarColorL[i] = p;
75 | }
76 | darken(0.7f, mBarColorL, mBarColorM);
77 | darken(0.5f, mBarColorL, mBarColorD);
78 |
79 | int i = 0;
80 | for (int v : new int[]{100, 75, 55, 50}) {
81 | Paint p = new Paint();
82 | p.setColor(Color.rgb(v, v, v));
83 | mBaseColorL[i++] = p;
84 | }
85 | darken(0.7f, mBaseColorL, mBaseColorM);
86 | darken(0.5f, mBaseColorL, mBaseColorD);
87 | }
88 |
89 | private void darken(float mult, Paint[] src, Paint[] dst) {
90 | if (src.length != dst.length) {
91 | throw new IllegalArgumentException("length mismatch");
92 | }
93 | for (int i = 0; i < src.length; i++) {
94 | int color = src[i].getColor();
95 | int r = (int) (Color.red(color) * mult);
96 | int g = (int) (Color.green(color) * mult);
97 | int b = (int) (Color.blue(color) * mult);
98 | Paint p = new Paint(src[i]);
99 | p.setColor(Color.argb(Color.alpha(color), r, g, b));
100 | dst[i] = p;
101 | }
102 | }
103 |
104 | @Override
105 | protected void onDraw(Canvas canvas) {
106 | final Phonon ph = mUiState != null ? mUiState.getPhonon() : null;
107 | final boolean isLocked = mUiState != null ? mUiState.getLocked() : false;
108 | final Path p = new Path();
109 |
110 | for (int i = 0; i < BAND_COUNT; i++) {
111 | float bar = ph != null ? ph.getBar(i) : .5f;
112 | float startX = bandToX(i);
113 | float stopX = startX + mBarWidth;
114 | float startY = barToY(bar);
115 | float midY = startY + mBarWidth;
116 |
117 | // Lower the brightness and contrast when locked.
118 | int baseCol = i % 2 + (isLocked ? 2 : 0);
119 |
120 | // Bar right (the top-left corner of this rectangle will be clipped.)
121 | float projX = mBarWidth * PROJECT_X;
122 | float projY = mBarWidth * PROJECT_Y;
123 | canvas.drawRect(stopX, midY + projY,stopX + projX, mHeight, mBaseColorD[baseCol]);
124 |
125 | // Bar front
126 | canvas.drawRect(startX, midY, stopX, mHeight, mBaseColorL[baseCol]);
127 |
128 | if (bar > 0) {
129 | // Cube right
130 | mCubeSide.offset(stopX, startY, p);
131 | canvas.drawPath(p, mBarColorD[i]);
132 |
133 | // Cube top
134 | mCubeTop.offset(startX, startY, p);
135 | canvas.drawPath(p, mBarColorM[i]);
136 |
137 | // Cube front
138 | canvas.drawRect(startX, startY, stopX, midY, mBarColorL[i]);
139 | } else {
140 | // Bar top
141 | mCubeTop.offset(startX, midY, p);
142 | canvas.drawPath(p, mBaseColorM[baseCol]);
143 | }
144 | }
145 | }
146 |
147 | private float mLastX;
148 | private float mLastY;
149 |
150 | @Override
151 | public boolean onTouchEvent(@NonNull MotionEvent event) {
152 | if (mUiState.getLocked()) {
153 | switch (event.getAction()) {
154 | case MotionEvent.ACTION_DOWN:
155 | mUiState.setLockBusy(true);
156 | return true;
157 | case MotionEvent.ACTION_UP:
158 | mUiState.setLockBusy(false);
159 | return true;
160 | case MotionEvent.ACTION_MOVE:
161 | return true;
162 | }
163 | return false;
164 | }
165 |
166 | switch (event.getAction()) {
167 | case MotionEvent.ACTION_DOWN:
168 | mLastX = event.getX();
169 | mLastY = event.getY();
170 | break;
171 | case MotionEvent.ACTION_UP:
172 | case MotionEvent.ACTION_MOVE:
173 | break;
174 | default:
175 | return false;
176 | }
177 |
178 | PhononMutable phm = mUiState.getPhononMutable();
179 | for (int i = 0; i < event.getHistorySize(); i++) {
180 | touchLine(phm, event.getHistoricalX(i), event.getHistoricalY(i));
181 | }
182 | touchLine(phm, event.getX(), event.getY());
183 |
184 | if (mUiState.sendIfDirty()) {
185 | invalidate();
186 | }
187 | return true;
188 | }
189 |
190 | @Override
191 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
192 | mWidth = getWidth();
193 | mHeight = getHeight();
194 | mBarWidth = mWidth / (BAND_COUNT + 2);
195 | mZeroLineY = mHeight * .9f;
196 | mCubeTop = projectCube(mBarWidth, true);
197 | mCubeSide = projectCube(mBarWidth, false);
198 | }
199 |
200 | // Draw the top or right side of a cube.
201 | private static Path projectCube(float unit, boolean isTop) {
202 | float projX = unit * PROJECT_X;
203 | float projY = unit * PROJECT_Y;
204 | Path p = new Path();
205 | p.moveTo(0, 0);
206 | p.lineTo(projX, projY);
207 | if (isTop) {
208 | // Top
209 | p.lineTo(unit + projX, projY);
210 | p.lineTo(unit, 0);
211 | } else {
212 | // Side
213 | p.lineTo(projX, unit + projY);
214 | p.lineTo(0, unit);
215 | }
216 | p.close();
217 | return p;
218 | }
219 |
220 | private float yToBar(float y) {
221 | float barHeight = 1f - (y / (mZeroLineY - mBarWidth));
222 | if (barHeight < 0) {
223 | return 0;
224 | }
225 | if (barHeight > 1) {
226 | return 1;
227 | }
228 | return barHeight;
229 | }
230 |
231 | private float barToY(float barHeight) {
232 | return (1f - barHeight) * (mZeroLineY - mBarWidth);
233 | }
234 |
235 | // Accepts 0 <= barIndex < BAND_COUNT,
236 | // leaving a 1-bar gap on each side of the screen.
237 | private float bandToX(int barIndex) {
238 | return mBarWidth * (barIndex + 1);
239 | }
240 |
241 | // Returns 0 <= out < BAND_COUNT,
242 | // leaving a 1-bar gap on each side of the screen.
243 | private int xToBand(float x) {
244 | int out = ((int) (x / mBarWidth)) - 1;
245 | if (out < 0) {
246 | out = 0;
247 | }
248 | if (out > BAND_COUNT - 1) {
249 | out = BAND_COUNT - 1;
250 | }
251 | return out;
252 |
253 | }
254 |
255 | // Starting bar?
256 | // Ending bar?
257 | // For each bar it exits:
258 | // set Y to exit-Y.
259 | // For the ending point:
260 | // set Y to final-Y.
261 |
262 | // Exits:
263 | // Right:
264 | // 0->3: 0, 1, 2 [endpoint in 3]
265 | // Left:
266 | // 3->0: 3, 2, 1 [endpoint in 0]
267 |
268 | private void touchLine(PhononMutable phm, float stopX, float stopY) {
269 | float startX = mLastX;
270 | float startY = mLastY;
271 | mLastX = stopX;
272 | mLastY = stopY;
273 | int startBand = xToBand(startX);
274 | int stopBand = xToBand(stopX);
275 | int direction = stopBand > startBand ? 1 : -1;
276 | for (int i = startBand; i != stopBand; i += direction) {
277 | // Get the x-coordinate where we exited band i.
278 | float exitX = bandToX(direction < 0 ? i : i + 1);
279 |
280 | // Get the Y value at exitX.
281 | float slope = (stopY - startY) / (stopX - startX);
282 | float exitY = startY + slope * (exitX - startX);
283 | phm.setBar(i, yToBar(exitY));
284 | }
285 | // Set the Y endpoint.
286 | phm.setBar(stopBand, yToBar(stopY));
287 | }
288 |
289 | @Override
290 | public void onLockStateChange(LockEvent e) {
291 | // Only spend time redrawing if this is an on/off event.
292 | if (e == LockEvent.TOGGLE) {
293 | invalidate();
294 | }
295 | }
296 | }
297 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/EqualizerViewLite.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.content.Context;
21 | import android.content.res.Resources;
22 | import android.graphics.Bitmap;
23 | import android.graphics.BitmapFactory;
24 | import android.graphics.Canvas;
25 | import android.graphics.Color;
26 | import android.graphics.Paint;
27 | import android.graphics.Path;
28 | import android.graphics.PorterDuff.Mode;
29 | import android.graphics.PorterDuffXfermode;
30 | import android.graphics.Rect;
31 | import android.util.AttributeSet;
32 | import android.util.TypedValue;
33 | import android.view.View;
34 |
35 | public class EqualizerViewLite extends View {
36 | private static final int BAND_COUNT = SpectrumData.BAND_COUNT;
37 |
38 | private Phonon mPhonon;
39 |
40 | private int mWidth;
41 | private int mHeight;
42 | private float mBarWidth;
43 |
44 | private Bitmap mBitmap = null;
45 |
46 | public EqualizerViewLite(Context context, AttributeSet attrs) {
47 | super(context, attrs);
48 | }
49 |
50 | public void setPhonon(Phonon ph) {
51 | if (mPhonon != ph) {
52 | mPhonon = ph;
53 | mBitmap = null;
54 | invalidate();
55 | }
56 | }
57 |
58 | private Bitmap makeBitmap() {
59 | Bitmap bmp = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
60 | Canvas canvas = new Canvas(bmp);
61 |
62 | // Draw a white line
63 | Paint whitePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
64 | whitePaint.setColor(Color.WHITE);
65 | whitePaint.setAlpha(isEnabled() ? 250 : 94);
66 | whitePaint.setStyle(Paint.Style.STROKE);
67 | whitePaint.setStrokeWidth(dpToPixels(3));
68 |
69 | Path path = new Path();
70 | boolean first = true;
71 | for (int i = 0; i < BAND_COUNT; i++) {
72 | float bar = mPhonon != null ? mPhonon.getBar(i) : .5f;
73 | float x = mBarWidth * (i + 0.5f);
74 | float y = barToY(bar);
75 |
76 | if (first) {
77 | first = false;
78 | path.moveTo(x, y);
79 | } else {
80 | path.lineTo(x, y);
81 | }
82 | }
83 | canvas.drawPath(path, whitePaint);
84 |
85 | // Overlay the spectrum bitmap to add color.
86 | Bitmap colorBmp = BitmapFactory.decodeResource(getResources(), R.drawable.spectrum);
87 | Rect src = new Rect(0, 0, colorBmp.getWidth(), colorBmp.getHeight());
88 | Rect dst = new Rect(0, 0, bmp.getWidth(), bmp.getHeight());
89 | Paint alphaPaint = new Paint();
90 | alphaPaint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
91 | canvas.drawBitmap(colorBmp, src, dst, alphaPaint);
92 |
93 | return bmp;
94 | }
95 |
96 | @Override
97 | protected void onDraw(Canvas canvas) {
98 | if (mBitmap == null) {
99 | mBitmap = makeBitmap();
100 | }
101 | canvas.drawBitmap(mBitmap, 0, 0, null);
102 | }
103 |
104 | @Override
105 | protected void onSizeChanged(int w, int h, int oldw, int oldh) {
106 | mWidth = getWidth();
107 | mHeight = getHeight();
108 | mBarWidth = (float) mWidth / BAND_COUNT;
109 | mBitmap = null;
110 | }
111 |
112 | private float barToY(float barHeight) {
113 | return (1f - barHeight) * mHeight;
114 | }
115 |
116 | private float dpToPixels(float dp) {
117 | Resources r = getResources();
118 | return TypedValue.applyDimension(
119 | TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
120 | }
121 |
122 | @Override
123 | public void setEnabled(boolean enabled) {
124 | super.setEnabled(enabled);
125 | mBitmap = null;
126 | invalidate();
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/FragmentIndex.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.content.Context;
21 |
22 | class FragmentIndex {
23 | static final int ID_CHROMA_DOZE = 0;
24 | static final int ID_OPTIONS = 1;
25 | static final int ID_MEMORY = 2;
26 | static final int ID_ABOUT = 3;
27 | static final int ID_COUNT = 4;
28 |
29 | static String[] getStrings(Context context) {
30 | String[] out = new String[ID_COUNT];
31 | out[ID_CHROMA_DOZE] = getPaddedString(context, R.string.app_name);
32 | out[ID_OPTIONS] = getPaddedString(context, R.string.options);
33 | out[ID_MEMORY] = getPaddedString(context, R.string.memory);
34 | out[ID_ABOUT] = getPaddedString(context, R.string.about_menu);
35 | return out;
36 | }
37 |
38 | private static String getPaddedString(Context context, int resId) {
39 | return context.getString(resId) + " ";
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/MainFragment.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.os.Bundle;
21 | import android.view.LayoutInflater;
22 | import android.view.View;
23 | import android.view.ViewGroup;
24 | import android.widget.ProgressBar;
25 | import android.widget.TextView;
26 |
27 | import androidx.fragment.app.Fragment;
28 |
29 | import java.text.DateFormat;
30 | import java.util.Date;
31 |
32 | public class MainFragment extends Fragment implements NoiseService.PercentListener {
33 | private EqualizerView mEqualizer;
34 | private TextView mStateText;
35 | private ProgressBar mPercentBar;
36 | private UIState mUiState;
37 |
38 | @Override
39 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
40 | Bundle savedInstanceState) {
41 | View v = inflater.inflate(R.layout.main_fragment, container, false);
42 |
43 | mEqualizer = (EqualizerView) v.findViewById(R.id.EqualizerView);
44 | mStateText = (TextView) v.findViewById(R.id.StateText);
45 | mPercentBar = (ProgressBar) v.findViewById(R.id.PercentBar);
46 | return v;
47 | }
48 |
49 | @Override
50 | public void onViewCreated(View view, Bundle savedInstanceState) {
51 | super.onViewCreated(view, savedInstanceState);
52 | mUiState = ((ChromaDoze) getActivity()).getUIState();
53 | mEqualizer.setUiState(mUiState);
54 | }
55 |
56 | @Override
57 | public void onResume() {
58 | super.onResume();
59 | // Start receiving progress events.
60 | NoiseService.addPercentListener(this);
61 | mUiState.addLockListener(mEqualizer);
62 |
63 | ((ChromaDoze) getActivity()).setFragmentId(FragmentIndex.ID_CHROMA_DOZE);
64 | }
65 |
66 | @Override
67 | public void onPause() {
68 | super.onPause();
69 | // Stop receiving progress events.
70 | NoiseService.removePercentListener(this);
71 | mUiState.removeLockListener(mEqualizer);
72 | }
73 |
74 | @Override
75 | public void onNoiseServicePercentChange(int percent, Date stopTimestamp, int stopReasonId) {
76 | boolean showGenerating = false;
77 | boolean showStopReason = false;
78 | if (percent < 0) {
79 | mPercentBar.setVisibility(View.INVISIBLE);
80 | // While the service is stopped, show what event caused it to stop.
81 | showStopReason = (stopReasonId != 0);
82 | } else if (percent < 100) {
83 | mPercentBar.setVisibility(View.VISIBLE);
84 | mPercentBar.setProgress(percent);
85 | showGenerating = true;
86 | } else {
87 | mPercentBar.setVisibility(View.INVISIBLE);
88 | // While the service is active, only the restart event is worth showing.
89 | showStopReason = (stopReasonId == R.string.stop_reason_restarted);
90 | }
91 | if (showStopReason) {
92 | // Expire the message after 12 hours, to avoid date ambiguity.
93 | long diff = new Date().getTime() - stopTimestamp.getTime();
94 | if (diff > 12 * 3600 * 1000L) {
95 | showStopReason = false;
96 | }
97 | }
98 | if (showGenerating) {
99 | mStateText.setText(R.string.generating);
100 | } else if (showStopReason) {
101 | String timeFmt = DateFormat.getTimeInstance(DateFormat.SHORT).format(stopTimestamp);
102 | mStateText.setText(timeFmt + ": " + getString(stopReasonId));
103 | } else {
104 | mStateText.setText("");
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/MemoryArrayAdapter.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.content.Context;
21 | import android.view.LayoutInflater;
22 | import android.view.View;
23 | import android.view.ViewGroup;
24 | import android.widget.ArrayAdapter;
25 | import android.widget.TextView;
26 |
27 | import java.util.List;
28 |
29 | public class MemoryArrayAdapter extends ArrayAdapter {
30 |
31 | enum Saved {YES, NO, NONE}
32 |
33 | public MemoryArrayAdapter(Context context, List objects) {
34 | super(context, 0, objects);
35 | }
36 |
37 | @Override
38 | public View getView(int position, View convertView, ViewGroup parent) {
39 | LayoutInflater inflater = (LayoutInflater)
40 | getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
41 |
42 | View view;
43 | if (convertView == null) {
44 | view = inflater.inflate(R.layout.memory_list_item, parent, false);
45 | } else {
46 | view = convertView;
47 | }
48 |
49 | initListItem(view, getItem(position), Saved.NONE);
50 |
51 | return view;
52 |
53 | }
54 |
55 | public void initListItem(View view, Phonon ph, Saved saved) {
56 | StringBuilder buf = new StringBuilder();
57 | if (ph.getMinVol() != 100) {
58 | buf.append(ph.getMinVolText());
59 | buf.append('\n');
60 | buf.append(ph.getPeriodText());
61 | if (saved != Saved.NONE) {
62 | buf.append('\n');
63 | }
64 | }
65 | if (saved == Saved.YES) {
66 | buf.append('\u21E9'); // Down arrow.
67 | } else if (saved == Saved.NO) {
68 | buf.append(getContext().getString(R.string.unsaved));
69 | }
70 | ((TextView) view.findViewById(R.id.text)).setText(buf.toString());
71 | ((EqualizerViewLite) view.findViewById(R.id.EqualizerView)).setPhonon(ph);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/MemoryFragment.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.os.Bundle;
21 | import android.view.LayoutInflater;
22 | import android.view.View;
23 | import android.view.View.OnClickListener;
24 | import android.view.ViewGroup;
25 | import android.widget.AdapterView;
26 | import android.widget.AdapterView.OnItemClickListener;
27 |
28 | import androidx.fragment.app.ListFragment;
29 |
30 | import com.mobeta.android.dslv.DragSortListView;
31 | import com.mobeta.android.dslv.DragSortListView.DropListener;
32 | import com.mobeta.android.dslv.DragSortListView.RemoveListener;
33 |
34 | import net.pmarks.chromadoze.MemoryArrayAdapter.Saved;
35 |
36 | public class MemoryFragment extends ListFragment implements
37 | OnItemClickListener, DropListener, RemoveListener {
38 |
39 | private View mHeaderView;
40 | private DragSortListView mDslv;
41 | private UIState mUiState;
42 |
43 | private MemoryArrayAdapter mAdapter;
44 |
45 | // This is basically the cached result of findScratchCopy().
46 | private final TrackedPosition mScratchPos = new TrackedPosition();
47 |
48 | @Override
49 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
50 | Bundle savedInstanceState) {
51 | mDslv = (DragSortListView) inflater.inflate(R.layout.memory_list,
52 | container, false);
53 |
54 | View v = inflater.inflate(R.layout.memory_list_item_top, null);
55 | View button = v.findViewById(R.id.save_button);
56 | button.setOnClickListener(new OnClickListener() {
57 | @Override
58 | public void onClick(View arg0) {
59 | // Clicked the "Save" button.
60 | final Phonon ph = mUiState.mScratchPhonon.makeMutableCopy();
61 | mAdapter.insert(ph, 0);
62 | // Gray out the header row.
63 | setScratchPosAndDraw(findScratchCopy());
64 | // Fake-click the header row.
65 | onItemClick(null, null, 0, 0);
66 | }
67 | });
68 | mHeaderView = v;
69 | mDslv.addHeaderView(mHeaderView, null, true);
70 |
71 | mDslv.addHeaderView(
72 | inflater.inflate(R.layout.memory_list_divider, null), null,
73 | false);
74 |
75 | return mDslv;
76 | }
77 |
78 | @Override
79 | public void onViewCreated(View view, Bundle savedInstanceState) {
80 | super.onViewCreated(view, savedInstanceState);
81 | mUiState = ((ChromaDoze) getActivity()).getUIState();
82 |
83 | mAdapter = new MemoryArrayAdapter(getActivity(), mUiState.mSavedPhonons);
84 | setListAdapter(mAdapter);
85 |
86 | mDslv.setOnItemClickListener(this);
87 | mDslv.setDropListener(this);
88 | mDslv.setRemoveListener(this);
89 | }
90 |
91 | @Override
92 | public void onResume() {
93 | super.onResume();
94 | ((ChromaDoze) getActivity()).setFragmentId(FragmentIndex.ID_MEMORY);
95 |
96 | setScratchPosAndDraw(findScratchCopy());
97 | syncActiveItem(false);
98 | }
99 |
100 | @Override
101 | public void drop(int from, int to) {
102 | if (from != to) {
103 | Phonon item = mAdapter.getItem(from);
104 | mAdapter.remove(item);
105 | mAdapter.insert(item, to);
106 | moveTrackedPositions(from, to, null);
107 | }
108 | }
109 |
110 | @Override
111 | public void remove(int which) {
112 | Phonon item = mAdapter.getItem(which);
113 | mAdapter.remove(item);
114 | moveTrackedPositions(which, TrackedPosition.NOWHERE, item);
115 | }
116 |
117 | private void moveTrackedPositions(int from, int to, Phonon deleted) {
118 | if ((to == TrackedPosition.NOWHERE) != (deleted != null)) {
119 | throw new IllegalArgumentException();
120 | }
121 | try {
122 | if (mUiState.mActivePos.move(from, to)) {
123 | // Move the radio button.
124 | syncActiveItem(false);
125 | }
126 | } catch (TrackedPosition.Deleted e) {
127 | // The active item was deleted!
128 | // Move it to scratch, so it can keep playing.
129 | mUiState.mScratchPhonon = deleted.makeMutableCopy();
130 | setScratchPosAndDraw(-1);
131 | mUiState.mActivePos.setPos(-1);
132 | syncActiveItem(true);
133 | return;
134 | }
135 |
136 | try {
137 | mScratchPos.move(from, to);
138 | } catch (TrackedPosition.Deleted e) {
139 | // The (inactive) scratch copy was deleted!
140 | // Reactivate the real scratch.
141 | setScratchPosAndDraw(-1);
142 | }
143 | }
144 |
145 | @Override
146 | public void onItemClick(AdapterView> parent, View view, int position,
147 | long id) {
148 | mUiState.setActivePhonon(position == 0 ?
149 | -1 : position - mDslv.getHeaderViewsCount());
150 |
151 | if (position == 0) {
152 | // User clicked on the scratch. Jump to a copy if one exists.
153 | syncActiveItem(true);
154 | }
155 | }
156 |
157 | // Header row should be grayed out whenever the scratch is redundant.
158 | private void setScratchPosAndDraw(int pos) {
159 | mScratchPos.setPos(pos);
160 | final boolean enabled = (pos == -1);
161 | mHeaderView.setEnabled(enabled);
162 | mAdapter.initListItem(mHeaderView, mUiState.mScratchPhonon,
163 | enabled ? Saved.NO : Saved.YES);
164 | }
165 |
166 | // If the scratch Phonon is unique, return -1. Otherwise, return the
167 | // copy's index within mArray.
168 | private int findScratchCopy() {
169 | final PhononMutable scratch = mUiState.mScratchPhonon;
170 | for (int i = 0; i < mUiState.mSavedPhonons.size(); i++) {
171 | if (mUiState.mSavedPhonons.get(i).fastEquals(scratch)) {
172 | return i;
173 | }
174 | }
175 | return -1;
176 | }
177 |
178 | private void syncActiveItem(boolean scrollThere) {
179 | // Determine which index to check.
180 | int index = mUiState.mActivePos.getPos();
181 | if (index == -1) {
182 | index = mScratchPos.getPos();
183 | mUiState.mActivePos.setPos(index);
184 | }
185 |
186 | // Convert the index to a list row.
187 | if (index == -1) {
188 | index = 0;
189 | } else {
190 | index += mDslv.getHeaderViewsCount();
191 | }
192 |
193 | // Modify the UI.
194 | mDslv.setItemChecked(index, true);
195 | if (scrollThere) {
196 | mDslv.smoothScrollToPosition(index);
197 | }
198 | }
199 | }
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/NoiseService.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2010 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.app.Notification;
21 | import android.app.PendingIntent;
22 | import android.app.Service;
23 | import android.content.Context;
24 | import android.content.Intent;
25 | import android.content.pm.ServiceInfo;
26 | import android.os.Build;
27 | import android.os.Handler;
28 | import android.os.IBinder;
29 | import android.os.Looper;
30 | import android.os.Message;
31 | import android.os.PowerManager;
32 | import android.os.Parcelable;
33 | import android.view.View;
34 | import android.widget.FrameLayout;
35 | import android.widget.RemoteViews;
36 | import android.widget.TextView;
37 |
38 | import androidx.core.app.NotificationChannelCompat;
39 | import androidx.core.app.NotificationCompat;
40 | import androidx.core.app.NotificationManagerCompat;
41 |
42 | import java.util.ArrayList;
43 | import java.util.Date;
44 |
45 | public class NoiseService extends Service {
46 | private static final int PERCENT_MSG = 1;
47 |
48 | // These must be accessed only from the main thread.
49 | private static int sLastPercent = -1;
50 | private static final ArrayList sPercentListeners = new ArrayList<>();
51 |
52 | // Save the reason for the most recent stop/restart. In theory, it would
53 | // be more correct to use persistent storage, but the values should stick
54 | // around in RAM long enough for practical purposes.
55 | private static Date sStopTimestamp = null;
56 | private static int sStopReasonId = 0;
57 |
58 | private SampleShuffler mSampleShuffler;
59 | private SampleGenerator mSampleGenerator;
60 | private AudioFocusHelper mAudioFocusHelper;
61 |
62 | private static final int NOTIFY_ID = 1;
63 | private PowerManager.WakeLock mWakeLock;
64 | private static final String CHANNEL_ID = "chromadoze_default";
65 |
66 | private int lastStartId = -1;
67 |
68 | private Handler mPercentHandler;
69 |
70 | private static class PercentHandler extends Handler {
71 |
72 | PercentHandler() {
73 | super(Looper.getMainLooper());
74 | }
75 |
76 | @Override
77 | public void handleMessage(Message msg) {
78 | if (msg.what != PERCENT_MSG) {
79 | throw new AssertionError("Unexpected message: " + msg.what);
80 | }
81 | updatePercent(msg.arg1);
82 | }
83 | }
84 |
85 | @Override
86 | @SuppressWarnings("WakelockTimeout")
87 | public void onCreate() {
88 | // Set up a message handler in the main thread.
89 | mPercentHandler = new PercentHandler();
90 | AudioParams params = new AudioParams();
91 | mSampleShuffler = new SampleShuffler(params);
92 | mSampleGenerator = new SampleGenerator(this, params, mSampleShuffler);
93 | PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
94 | mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "chromadoze:NoiseService");
95 | mWakeLock.acquire();
96 |
97 | final CharSequence name = getString(R.string.channel_name);
98 | final String description = getString(R.string.channel_description);
99 | final int importance = NotificationManagerCompat.IMPORTANCE_LOW;
100 | NotificationManagerCompat.from(this).createNotificationChannel(
101 | new NotificationChannelCompat.Builder(CHANNEL_ID, importance)
102 | .setName(name)
103 | .setDescription(description)
104 | .build());
105 |
106 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
107 | startForeground(NOTIFY_ID, makeNotify(), ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK);
108 | } else {
109 | startForeground(NOTIFY_ID, makeNotify());
110 | }
111 |
112 | // Note: This leaks memory if I use "this" instead of "getApplicationContext()".
113 | mAudioFocusHelper = new AudioFocusHelper(
114 | getApplicationContext(), mSampleShuffler.getVolumeListener());
115 | }
116 |
117 | @SuppressWarnings("deprecation")
118 | private static T getParcelableExtraCompat(Intent intent, String name, Class clazz) {
119 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
120 | return intent.getParcelableExtra(name, clazz);
121 | } else {
122 | return intent.getParcelableExtra(name);
123 | }
124 | }
125 |
126 | @SuppressWarnings("deprecation")
127 | private void stopForegroundCompat() {
128 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
129 | stopForeground(STOP_FOREGROUND_REMOVE);
130 | } else {
131 | stopForeground(true);
132 | }
133 | }
134 |
135 | @Override
136 | public int onStartCommand(Intent intent, int flags, int startId) {
137 | // When multiple spectra arrive, only the latest should remain active.
138 | if (lastStartId >= 0) {
139 | stopSelf(lastStartId);
140 | lastStartId = -1;
141 | }
142 |
143 | // Handle the Stop intent.
144 | int stopReasonId = intent.getIntExtra("stopReasonId", 0);
145 | if (stopReasonId != 0) {
146 | saveStopReason(stopReasonId);
147 | stopSelf(startId);
148 | return START_NOT_STICKY;
149 | }
150 |
151 | // Notify the user that the OS restarted the process.
152 | if ((flags & START_FLAG_REDELIVERY) != 0) {
153 | saveStopReason(R.string.stop_reason_restarted);
154 | }
155 |
156 | SpectrumData spectrum = getParcelableExtraCompat(intent, "spectrum", SpectrumData.class);
157 |
158 | // Synchronous updates.
159 | mSampleShuffler.setAmpWave(
160 | intent.getFloatExtra("minvol", -1),
161 | intent.getFloatExtra("period", -1));
162 | mSampleShuffler.getVolumeListener().setVolumeLevel(
163 | intent.getFloatExtra("volumeLimit", -1));
164 | mAudioFocusHelper.setActive(
165 | !intent.getBooleanExtra("ignoreAudioFocus", false));
166 |
167 | // Background updates.
168 | mSampleGenerator.updateSpectrum(spectrum);
169 |
170 | // If the kernel decides to kill this process, let Android restart it
171 | // using the most-recent spectrum. It's important that we call
172 | // stopSelf() with this startId when a replacement spectrum arrives,
173 | // or if we're stopping the service intentionally.
174 | lastStartId = startId;
175 | return START_REDELIVER_INTENT;
176 | }
177 |
178 | @Override
179 | public void onDestroy() {
180 | if (lastStartId != -1) {
181 | // This condition can be triggered from adb shell:
182 | // $ am stopservice net.pmarks.chromadoze/.NoiseService
183 | saveStopReason(R.string.stop_reason_mysterious);
184 | }
185 |
186 | mSampleGenerator.stopThread();
187 | mSampleShuffler.stopThread();
188 |
189 | mPercentHandler.removeMessages(PERCENT_MSG);
190 | updatePercent(-1);
191 | mAudioFocusHelper.setActive(false);
192 | stopForegroundCompat();
193 | mWakeLock.release();
194 | }
195 |
196 | @Override
197 | public IBinder onBind(Intent intent) {
198 | // Don't use binding.
199 | return null;
200 | }
201 |
202 | // Create an icon for the notification bar.
203 | private Notification makeNotify() {
204 | NotificationCompat.Builder b = new NotificationCompat.Builder(this, CHANNEL_ID)
205 | .setSmallIcon(R.drawable.ic_stat_bars)
206 | .setWhen(0)
207 | .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
208 | .setContentIntent(PendingIntent.getActivity(
209 | this,
210 | 0,
211 | new Intent(this, ChromaDoze.class)
212 | .setAction(Intent.ACTION_MAIN)
213 | .addCategory(Intent.CATEGORY_LAUNCHER),
214 | PendingIntent.FLAG_IMMUTABLE));
215 |
216 | RemoteViews rv = new RemoteViews(
217 | getPackageName(), R.layout.notification_with_stop_button);
218 | PendingIntent pendingIntent = PendingIntent.getService(
219 | this,
220 | 0,
221 | newStopIntent(this, R.string.stop_reason_notification),
222 | PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
223 | rv.setOnClickPendingIntent(R.id.stop_button, pendingIntent);
224 |
225 | // Temporarily inflate the notification, to copy colors from its default style.
226 | final View inflated = rv.apply(this, new FrameLayout(this));
227 | final TextView titleText = inflated.findViewById(R.id.title);
228 | rv.setInt(R.id.divider, "setBackgroundColor", titleText.getTextColors().getDefaultColor());
229 | rv.setInt(R.id.stop_button_square, "setBackgroundColor", titleText.getTextColors().getDefaultColor());
230 |
231 | // It would be nice if there were some way to omit the "expander affordance",
232 | // but this seems good enough.
233 | b.setCustomContentView(rv);
234 | b.setStyle(new NotificationCompat.DecoratedCustomViewStyle());
235 |
236 | return b.build();
237 | }
238 |
239 | // Call updatePercent() from any thread.
240 | public void updatePercentAsync(int percent) {
241 | mPercentHandler.removeMessages(PERCENT_MSG);
242 | Message m = Message.obtain(mPercentHandler, PERCENT_MSG);
243 | m.arg1 = percent;
244 | m.sendToTarget();
245 | }
246 |
247 | // If connected, notify the main activity of our progress.
248 | // This must run in the main thread.
249 | private static void updatePercent(int percent) {
250 | for (PercentListener listener : sPercentListeners) {
251 | listener.onNoiseServicePercentChange(percent, sStopTimestamp, sStopReasonId);
252 | }
253 | sLastPercent = percent;
254 | }
255 |
256 | // Connect the main activity so it receives progress updates.
257 | // This must run in the main thread.
258 | public static void addPercentListener(PercentListener listener) {
259 | sPercentListeners.add(listener);
260 | listener.onNoiseServicePercentChange(sLastPercent, sStopTimestamp, sStopReasonId);
261 | }
262 |
263 | public static void removePercentListener(PercentListener listener) {
264 | if (!sPercentListeners.remove(listener)) {
265 | throw new IllegalStateException();
266 | }
267 | }
268 |
269 | public interface PercentListener {
270 | void onNoiseServicePercentChange(int percent, Date stopTimestamp, int stopReasonId);
271 | }
272 |
273 | private static Intent newStopIntent(Context ctx, int stopReasonId) {
274 | return new Intent(ctx, NoiseService.class).putExtra("stopReasonId", stopReasonId);
275 | }
276 |
277 | public static void stopNow(Context ctx, int stopReasonId) {
278 | try {
279 | ctx.startService(newStopIntent(ctx, stopReasonId));
280 | } catch (IllegalStateException e) {
281 | // This can be triggered by running "adb shell input keyevent 86" when the app
282 | // is not running. We ignore it, because in that case there's nothing to stop.
283 | }
284 | }
285 |
286 | private static void saveStopReason(int stopReasonId) {
287 | sStopTimestamp = new Date();
288 | sStopReasonId = stopReasonId;
289 | }
290 | }
291 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/OptionsFragment.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2011 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.os.Bundle;
21 | import android.view.LayoutInflater;
22 | import android.view.View;
23 | import android.view.ViewGroup;
24 | import android.widget.CompoundButton;
25 | import android.widget.CompoundButton.OnCheckedChangeListener;
26 | import android.widget.SeekBar;
27 | import android.widget.SeekBar.OnSeekBarChangeListener;
28 | import android.widget.TextView;
29 |
30 | import androidx.appcompat.widget.SwitchCompat;
31 | import androidx.fragment.app.Fragment;
32 |
33 | public class OptionsFragment extends Fragment implements OnSeekBarChangeListener, OnCheckedChangeListener {
34 | private UIState mUiState;
35 | private SeekBar mMinVolSeek;
36 | private TextView mMinVolText;
37 | private SeekBar mPeriodSeek;
38 | private TextView mPeriodText;
39 | private SwitchCompat mAutoPlayCheck;
40 | private SwitchCompat mIgnoreAudioFocusCheck;
41 | private SwitchCompat mVolumeLimitCheck;
42 | private SeekBar mVolumeLimitSeek;
43 |
44 | @Override
45 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
46 | Bundle savedInstanceState) {
47 | View v = inflater.inflate(R.layout.options_fragment, container, false);
48 |
49 | mMinVolSeek = (SeekBar) v.findViewById(R.id.MinVolSeek);
50 | mMinVolText = (TextView) v.findViewById(R.id.MinVolText);
51 | mPeriodSeek = (SeekBar) v.findViewById(R.id.PeriodSeek);
52 | mPeriodText = (TextView) v.findViewById(R.id.PeriodText);
53 |
54 | mAutoPlayCheck = (SwitchCompat) v.findViewById(R.id.AutoPlayCheck);
55 |
56 | mIgnoreAudioFocusCheck = (SwitchCompat) v.findViewById(R.id.IgnoreAudioFocusCheck);
57 | mVolumeLimitCheck = (SwitchCompat) v.findViewById(R.id.VolumeLimitCheck);
58 | mVolumeLimitSeek = (SeekBar) v.findViewById(R.id.VolumeLimitSeek);
59 |
60 | return v;
61 | }
62 |
63 | @Override
64 | public void onViewCreated(View view, Bundle savedInstanceState) {
65 | super.onViewCreated(view, savedInstanceState);
66 | mUiState = ((ChromaDoze) getActivity()).getUIState();
67 | final Phonon ph = mUiState.getPhonon();
68 |
69 | mMinVolText.setText(ph.getMinVolText());
70 | mMinVolSeek.setProgress(ph.getMinVol());
71 | mMinVolSeek.setOnSeekBarChangeListener(this);
72 |
73 | mPeriodText.setText(ph.getPeriodText());
74 | mPeriodSeek.setProgress(ph.getPeriod());
75 | // When the volume is at 100%, disable the period bar.
76 | mPeriodSeek.setEnabled(ph.getMinVol() != 100);
77 | mPeriodSeek.setMax(PhononMutable.PERIOD_MAX);
78 | mPeriodSeek.setOnSeekBarChangeListener(this);
79 |
80 | mAutoPlayCheck.setChecked(mUiState.getAutoPlay());
81 | mAutoPlayCheck.setOnCheckedChangeListener(this);
82 |
83 | mIgnoreAudioFocusCheck.setChecked(mUiState.getIgnoreAudioFocus());
84 | mIgnoreAudioFocusCheck.setOnCheckedChangeListener(this);
85 |
86 | mVolumeLimitCheck.setOnCheckedChangeListener(this);
87 | mVolumeLimitSeek.setMax(UIState.MAX_VOLUME);
88 | mVolumeLimitSeek.setOnSeekBarChangeListener(this);
89 | redrawVolumeLimit();
90 | }
91 |
92 | @Override
93 | public void onResume() {
94 | super.onResume();
95 | ((ChromaDoze) getActivity()).setFragmentId(FragmentIndex.ID_OPTIONS);
96 | }
97 |
98 | @Override
99 | public void onProgressChanged(SeekBar seekBar, int progress,
100 | boolean fromUser) {
101 | if (!fromUser) {
102 | return;
103 | }
104 | if (seekBar == mVolumeLimitSeek) {
105 | mUiState.setVolumeLimit(progress);
106 | redrawVolumeLimit();
107 | } else {
108 | final PhononMutable phm = mUiState.getPhononMutable();
109 | if (seekBar == mMinVolSeek) {
110 | phm.setMinVol(progress);
111 | mMinVolText.setText(phm.getMinVolText());
112 | mPeriodSeek.setEnabled(progress != 100);
113 | } else if (seekBar == mPeriodSeek) {
114 | phm.setPeriod(progress);
115 | mPeriodText.setText(phm.getPeriodText());
116 | }
117 | }
118 | mUiState.sendIfDirty();
119 | }
120 |
121 | @Override
122 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
123 | if (buttonView == mAutoPlayCheck) {
124 | mUiState.setAutoPlay(isChecked, true);
125 | } else if (buttonView == mIgnoreAudioFocusCheck) {
126 | mUiState.setIgnoreAudioFocus(isChecked);
127 | } else if (buttonView == mVolumeLimitCheck) {
128 | mUiState.setVolumeLimitEnabled(isChecked);
129 | redrawVolumeLimit();
130 | }
131 | mUiState.sendIfDirty();
132 | }
133 |
134 | @Override
135 | public void onStartTrackingTouch(SeekBar seekBar) {
136 | }
137 |
138 | @Override
139 | public void onStopTrackingTouch(SeekBar seekBar) {
140 | }
141 |
142 | private void redrawVolumeLimit() {
143 | boolean enabled = mUiState.getVolumeLimitEnabled();
144 | mVolumeLimitCheck.setChecked(enabled);
145 | mVolumeLimitSeek.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
146 | mVolumeLimitSeek.setProgress(mUiState.getVolumeLimit());
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/Phonon.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.content.Intent;
21 |
22 | // Read-only view of a PhononMutable
23 | public interface Phonon {
24 | public String toJSON();
25 |
26 | public boolean isSilent();
27 |
28 | public float getBar(int band);
29 |
30 | public int getMinVol();
31 |
32 | public String getMinVolText();
33 |
34 | public int getPeriod();
35 |
36 | public String getPeriodText();
37 |
38 | public PhononMutable makeMutableCopy();
39 |
40 | public void writeIntent(Intent intent);
41 |
42 | public boolean fastEquals(Phonon other);
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/PhononMutable.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.content.Intent;
21 | import android.content.SharedPreferences;
22 |
23 | import org.json.JSONArray;
24 | import org.json.JSONException;
25 | import org.json.JSONObject;
26 |
27 | import java.util.Arrays;
28 | import java.util.Locale;
29 |
30 | // A Phonon represents a collection of user-tweakable sound
31 | // information, presented as a single row in the "Memory" view.
32 | //
33 | // Supported operations:
34 | // - Convert to/from JSON for storage.
35 | // - Efficient equality testing.
36 | // - Convert to SpectrumData for playback.
37 | // - Get and set sound-related values.
38 | public class PhononMutable implements Phonon {
39 | private static final int BAND_COUNT = SpectrumData.BAND_COUNT;
40 |
41 | // The current value of each bar, [0, 1023]
42 | private static final short BAR_MAX = 1023;
43 | private final short mBars[] = new short[BAND_COUNT];
44 |
45 | private int mMinVol = 100;
46 |
47 | public static final int PERIOD_MAX = 53; // Maps to 60 seconds.
48 | private int mPeriod = 18; // Maps to 1 second
49 |
50 | private boolean mDirty = true;
51 | private int mHash;
52 |
53 | public void resetToDefault() {
54 | for (int i = 0; i < BAND_COUNT; i++) {
55 | setBar(i, .5f);
56 | }
57 | mMinVol = 100;
58 | mPeriod = 18;
59 | cleanMe();
60 | }
61 |
62 | // Load data from <= Chroma Doze 2.2.
63 | public boolean loadFromLegacyPrefs(SharedPreferences pref) {
64 | if (pref.getFloat("barHeight0", -1) < 0) {
65 | return false;
66 | }
67 | for (int i = 0; i < BAND_COUNT; i++) {
68 | setBar(i, pref.getFloat("barHeight" + i, .5f));
69 | }
70 | setMinVol(pref.getInt("minVol", 100));
71 | setPeriod(pref.getInt("period", 18));
72 | cleanMe();
73 | return true;
74 | }
75 |
76 | public boolean loadFromJSON(String jsonString) {
77 | if (jsonString == null) {
78 | return false;
79 | }
80 | try {
81 | JSONObject j = new JSONObject(jsonString);
82 | setMinVol(j.getInt("minvol"));
83 | setPeriod(j.getInt("period"));
84 | JSONArray jBars = j.getJSONArray("bars");
85 | for (int i = 0; i < BAND_COUNT; i++) {
86 | final int b = jBars.getInt(i);
87 | if (!(0 <= b && b <= BAR_MAX)) {
88 | return false;
89 | }
90 | mBars[i] = (short) b;
91 | }
92 | } catch (JSONException e) {
93 | return false;
94 | }
95 | cleanMe();
96 | return true;
97 | }
98 |
99 | // Storing everything as text might be useful if I ever want
100 | // to do an export feature.
101 | @Override
102 | public String toJSON() {
103 | try {
104 | JSONObject j = new JSONObject();
105 | JSONArray jBars = new JSONArray();
106 | for (short s : mBars) {
107 | jBars.put(s);
108 | }
109 | j.put("bars", jBars);
110 | j.put("minvol", mMinVol);
111 | j.put("period", mPeriod);
112 | return j.toString();
113 | } catch (JSONException e) {
114 | throw new RuntimeException("impossible");
115 | }
116 | }
117 |
118 | // band: The index number of the bar.
119 | // value: Between 0 and 1.
120 | public void setBar(int band, float value) {
121 | // Map from 0..1 to a discrete short.
122 | short sval;
123 | if (value <= 0f) {
124 | sval = 0;
125 | } else if (value >= 1f) {
126 | sval = BAR_MAX;
127 | } else {
128 | sval = (short) (value * BAR_MAX);
129 | }
130 | if (mBars[band] != sval) {
131 | mBars[band] = sval;
132 | mDirty = true;
133 | }
134 | }
135 |
136 | @Override
137 | public float getBar(int band) {
138 | return mBars[band] / (float) BAR_MAX;
139 | }
140 |
141 | private float[] getAllBars() {
142 | float[] out = new float[BAND_COUNT];
143 | for (int i = 0; i < BAND_COUNT; i++) {
144 | out[i] = getBar(i);
145 | }
146 | return out;
147 | }
148 |
149 | // Return true if all equalizer bars are set to zero.
150 | @Override
151 | public boolean isSilent() {
152 | for (int i = 0; i < BAND_COUNT; i++) {
153 | if (mBars[i] > 0) {
154 | return false;
155 | }
156 | }
157 | return true;
158 | }
159 |
160 | // Range: [0, 100]
161 | public void setMinVol(int minVol) {
162 | if (minVol < 0) {
163 | minVol = 0;
164 | } else if (minVol > 100) {
165 | minVol = 100;
166 | }
167 | if (minVol != mMinVol) {
168 | mMinVol = minVol;
169 | mDirty = true;
170 | }
171 | }
172 |
173 | @Override
174 | public int getMinVol() {
175 | return mMinVol;
176 | }
177 |
178 | @Override
179 | public String getMinVolText() {
180 | return mMinVol + "%";
181 | }
182 |
183 | public void setPeriod(int period) {
184 | if (period < 0) {
185 | period = 0;
186 | } else if (period > PERIOD_MAX) {
187 | period = PERIOD_MAX;
188 | }
189 | if (period != mPeriod) {
190 | mPeriod = period;
191 | mDirty = true;
192 | }
193 | }
194 |
195 | // This gets the slider position.
196 | @Override
197 | public int getPeriod() {
198 | return mPeriod;
199 | }
200 |
201 | @Override
202 | public String getPeriodText() {
203 | // This probably isn't very i18n friendly.
204 | float s = getPeriodSeconds();
205 | if (s >= 1f) {
206 | return String.format(Locale.getDefault(), "%.2g sec", s);
207 | } else {
208 | return String.format(Locale.getDefault(), "%d ms", Math.round(s * 1000));
209 | }
210 | }
211 |
212 | private float getPeriodSeconds() {
213 | // This is a somewhat human-friendly mapping from
214 | // scroll position to seconds.
215 | if (mPeriod < 9) {
216 | // 10ms, 20ms, ..., 90ms
217 | return (mPeriod + 1) * .010f;
218 | } else if (mPeriod < 18) {
219 | // 100ms, 200ms, ..., 900ms
220 | return (mPeriod - 9 + 1) * .100f;
221 | } else if (mPeriod < 36) {
222 | // 1.0s, 1.5s, ..., 9.5s
223 | return (mPeriod - 18 + 2) * .5f;
224 | } else if (mPeriod < 45) {
225 | // 10, 11, ..., 19
226 | return (mPeriod - 36 + 10) * 1f;
227 | } else {
228 | // 20, 25, 30, ... 60
229 | return (mPeriod - 45 + 4) * 5f;
230 | }
231 | }
232 |
233 | @Override
234 | public PhononMutable makeMutableCopy() {
235 | if (mDirty) {
236 | throw new IllegalStateException();
237 | }
238 | PhononMutable c = new PhononMutable();
239 | System.arraycopy(mBars, 0, c.mBars, 0, BAND_COUNT);
240 | c.mMinVol = mMinVol;
241 | c.mPeriod = mPeriod;
242 | c.mHash = mHash;
243 | c.mDirty = false;
244 | return c;
245 | }
246 |
247 | public boolean isDirty() {
248 | return mDirty;
249 | }
250 |
251 | // We assume that all Intents will be sent to the service,
252 | // so this also clears the dirty bit.
253 | @Override
254 | public void writeIntent(Intent intent) {
255 | intent.putExtra("spectrum", new SpectrumData(getAllBars()));
256 | intent.putExtra("minvol", mMinVol / 100f);
257 | intent.putExtra("period", getPeriodSeconds());
258 | cleanMe();
259 | }
260 |
261 | private void cleanMe() {
262 | int h = Arrays.hashCode(mBars);
263 | h = 31 * h + mMinVol;
264 | h = 31 * h + mPeriod;
265 | mHash = h;
266 | mDirty = false;
267 | }
268 |
269 | @Override
270 | public boolean fastEquals(Phonon other) {
271 | PhononMutable o = (PhononMutable) other;
272 | if (mDirty || o.mDirty) {
273 | throw new IllegalStateException();
274 | }
275 | if (this == o) {
276 | return true;
277 | }
278 | return (mHash == o.mHash &&
279 | mMinVol == o.mMinVol &&
280 | mPeriod == o.mPeriod &&
281 | Arrays.equals(mBars, o.mBars));
282 | }
283 |
284 | }
285 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/SampleGenerator.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2010 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.os.Process;
21 | import android.os.SystemClock;
22 |
23 | import org.jtransforms.dct.FloatDCT_1D;
24 |
25 | class SampleGenerator {
26 | private final NoiseService mNoiseService;
27 | private final AudioParams mParams;
28 | private final SampleShuffler mSampleShuffler;
29 | private final Thread mWorkerThread;
30 |
31 | // Communication variables; must be synchronized.
32 | private boolean mStopping;
33 | private SpectrumData mPendingSpectrum;
34 |
35 | // Variables accessed from the thread only.
36 | private int mLastDctSize = -1;
37 | private FloatDCT_1D mDct;
38 | private final XORShiftRandom mRandom = new XORShiftRandom(); // Not thread safe.
39 |
40 | public SampleGenerator(NoiseService noiseService, AudioParams params,
41 | SampleShuffler sampleShuffler) {
42 | mNoiseService = noiseService;
43 | mParams = params;
44 | mSampleShuffler = sampleShuffler;
45 |
46 | mWorkerThread = new Thread("SampleGeneratorThread") {
47 | @Override
48 | public void run() {
49 | try {
50 | threadLoop();
51 | } catch (StopException e) {
52 | }
53 | }
54 | };
55 | mWorkerThread.start();
56 | }
57 |
58 | public void stopThread() {
59 | synchronized (this) {
60 | mStopping = true;
61 | notify();
62 | }
63 | try {
64 | mWorkerThread.join();
65 | } catch (InterruptedException e) {
66 | }
67 | }
68 |
69 | public synchronized void updateSpectrum(SpectrumData spectrum) {
70 | mPendingSpectrum = spectrum;
71 | notify();
72 | }
73 |
74 | private void threadLoop() throws StopException {
75 | Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
76 |
77 | // Chunk-making progress:
78 | final SampleGeneratorState state = new SampleGeneratorState();
79 | SpectrumData spectrum = null;
80 | long waitMs = -1;
81 |
82 | while (true) {
83 | // This does one of 3 things:
84 | // - Throw StopException if stopThread() was called.
85 | // - Check if a new spectrum is waiting.
86 | // - Block if there's no work to do.
87 | final SpectrumData newSpectrum = popPendingSpectrum(waitMs);
88 | if (newSpectrum != null && !newSpectrum.sameSpectrum(spectrum)) {
89 | spectrum = newSpectrum;
90 | state.reset();
91 | mNoiseService.updatePercentAsync(state.getPercent());
92 | } else if (waitMs == -1) {
93 | // Nothing changed. Keep waiting.
94 | continue;
95 | }
96 |
97 | final long startMs = SystemClock.elapsedRealtime();
98 |
99 | // Generate the next chunk of sound.
100 | float[] dctData = doIDCT(state.getChunkSize(), spectrum);
101 | if (mSampleShuffler.handleChunk(dctData, state.getStage())) {
102 | // Not dropped.
103 | state.advance();
104 | mNoiseService.updatePercentAsync(state.getPercent());
105 | }
106 |
107 | // Avoid burning the CPU while the user is scrubbing. For the
108 | // first couple large chunks, the next chunk should be ready
109 | // when this one is ~75% finished playing.
110 | final long sleepTargetMs = state.getSleepTargetMs(mParams.SAMPLE_RATE);
111 | final long elapsedMs = SystemClock.elapsedRealtime() - startMs;
112 | waitMs = sleepTargetMs - elapsedMs;
113 | if (waitMs < 0) waitMs = 0;
114 | if (waitMs > sleepTargetMs) waitMs = sleepTargetMs;
115 |
116 | if (state.done()) {
117 | // No chunks left; save RAM.
118 | mDct = null;
119 | mLastDctSize = -1;
120 | waitMs = -1;
121 | }
122 | }
123 | }
124 |
125 | private synchronized SpectrumData popPendingSpectrum(long waitMs)
126 | throws StopException {
127 | if (waitMs != 0 && !mStopping && mPendingSpectrum == null) {
128 | // Wait once. The retry loop is in the caller.
129 | try {
130 | if (waitMs < 0) {
131 | wait(/*forever*/);
132 | } else {
133 | wait(waitMs);
134 | }
135 | } catch (InterruptedException e) {
136 | }
137 | }
138 | if (mStopping) {
139 | throw new StopException();
140 | }
141 | try {
142 | return mPendingSpectrum;
143 | } finally {
144 | mPendingSpectrum = null;
145 | }
146 | }
147 |
148 | private float[] doIDCT(int dctSize, SpectrumData spectrum) {
149 | if (dctSize != mLastDctSize) {
150 | mDct = new FloatDCT_1D(dctSize);
151 | mLastDctSize = dctSize;
152 | }
153 | float[] dctData = new float[dctSize];
154 |
155 | spectrum.fill(dctData, mParams.SAMPLE_RATE);
156 |
157 | // Multiply by a block of white noise.
158 | for (int i = 0; i < dctSize; ) {
159 | long rand = mRandom.nextLong();
160 | for (int b = 0; b < 8; b++) {
161 | dctData[i++] *= (byte) rand / 128f;
162 | rand >>= 8;
163 | }
164 | }
165 |
166 | mDct.inverse(dctData, false);
167 | return dctData;
168 | }
169 |
170 | private static class StopException extends Exception {
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/SampleGeneratorState.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2010 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | class SampleGeneratorState {
21 | // List of possible chunk stages.
22 | public static final int S_FIRST_SMALL = 0;
23 | public static final int S_OTHER_SMALL = 1;
24 | public static final int S_FIRST_VOLUME = 2;
25 | public static final int S_OTHER_VOLUME = 3;
26 | public static final int S_LAST_VOLUME = 4;
27 | public static final int S_LARGE_NOCLIP = 5;
28 |
29 | // How many small preview chunks to generate at first.
30 | private static final int N_SMALL_CHUNKS = 4;
31 |
32 | // How many final full-size chunks to generate.
33 | private static final int N_LARGE_CHUNKS = 20;
34 |
35 | // How many chunks overall.
36 | private static final int N_TOTAL_CHUNKS = N_SMALL_CHUNKS + N_LARGE_CHUNKS;
37 |
38 | // How many large chunks to use for estimating the global volume.
39 | private static final int N_VOLUME_CHUNKS = 4;
40 |
41 | // Size of small/large chunks, in samples.
42 | private static final int SMALL_CHUNK_SIZE = 8192;
43 | private static final int LARGE_CHUNK_SIZE = 65536;
44 |
45 | // Begin in the "done" state.
46 | private int mChunkNumber = N_TOTAL_CHUNKS;
47 |
48 | public void reset() {
49 | mChunkNumber = 0;
50 | }
51 |
52 | public void advance() {
53 | mChunkNumber++;
54 | }
55 |
56 | public boolean done() {
57 | return mChunkNumber >= N_TOTAL_CHUNKS;
58 | }
59 |
60 | public int getStage() {
61 | if (mChunkNumber < N_SMALL_CHUNKS) {
62 | // Small chunk.
63 | switch (mChunkNumber) {
64 | case 0:
65 | return S_FIRST_SMALL;
66 | default:
67 | return S_OTHER_SMALL;
68 | }
69 | } else if (mChunkNumber < N_SMALL_CHUNKS + N_VOLUME_CHUNKS) {
70 | // Large chunk, with volume computation.
71 | switch (mChunkNumber) {
72 | case N_SMALL_CHUNKS:
73 | return S_FIRST_VOLUME;
74 | case N_SMALL_CHUNKS + N_VOLUME_CHUNKS - 1:
75 | return S_LAST_VOLUME;
76 | default:
77 | return S_OTHER_VOLUME;
78 | }
79 | } else {
80 | // Large chunk, volume already set.
81 | return S_LARGE_NOCLIP;
82 | }
83 | }
84 |
85 | public int getPercent() {
86 | return mChunkNumber * 100 / N_TOTAL_CHUNKS;
87 | }
88 |
89 | public int getChunkSize() {
90 | return mChunkNumber < N_SMALL_CHUNKS ? SMALL_CHUNK_SIZE : LARGE_CHUNK_SIZE;
91 | }
92 |
93 | // For the first couple large chunks, returns 75% of the chunk duration.
94 | public long getSleepTargetMs(int sampleRate) {
95 | final int i = mChunkNumber - N_SMALL_CHUNKS;
96 | if (0 <= i && i < 2) {
97 | return 750 * LARGE_CHUNK_SIZE / sampleRate;
98 | }
99 | return 0;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/SpectrumData.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2010 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.os.Parcel;
21 | import android.os.Parcelable;
22 |
23 | // SpectrumData is a Phonon translated into "machine readable" form.
24 | //
25 | // In other words, the values here are suitable for generating noise,
26 | // and not for storage or rendering UI elements.
27 |
28 | public class SpectrumData implements Parcelable {
29 | public static final Parcelable.Creator CREATOR
30 | = new Parcelable.Creator() {
31 | @Override
32 | public SpectrumData createFromParcel(Parcel in) {
33 | return new SpectrumData(in);
34 | }
35 |
36 | @Override
37 | public SpectrumData[] newArray(int size) {
38 | return new SpectrumData[size];
39 | }
40 | };
41 |
42 | private static final float MIN_FREQ = 100;
43 | private static final float MAX_FREQ = 20000;
44 | public static final int BAND_COUNT = 32;
45 |
46 | // The frequency of the edges between each bar.
47 | private static final int[] EDGE_FREQS = calculateEdgeFreqs();
48 |
49 | private final float[] mData;
50 |
51 | private static int[] calculateEdgeFreqs() {
52 | int[] edgeFreqs = new int[BAND_COUNT + 1];
53 | float range = MAX_FREQ / MIN_FREQ;
54 | for (int i = 0; i <= BAND_COUNT; i++) {
55 | edgeFreqs[i] = (int) (MIN_FREQ * Math.pow(range, (float) i / BAND_COUNT));
56 | }
57 | return edgeFreqs;
58 | }
59 |
60 | public SpectrumData(float[] donateBars) {
61 | if (donateBars.length != BAND_COUNT) {
62 | throw new RuntimeException("Incorrect number of bands");
63 | }
64 | mData = donateBars;
65 | for (int i = 0; i < BAND_COUNT; i++) {
66 | if (mData[i] <= 0f) {
67 | mData[i] = 0f;
68 | } else {
69 | mData[i] = 0.001f * (float) Math.pow(1000, mData[i]);
70 | }
71 | }
72 | }
73 |
74 | private SpectrumData(Parcel in) {
75 | mData = new float[BAND_COUNT];
76 | in.readFloatArray(mData);
77 | }
78 |
79 | @Override
80 | public int describeContents() {
81 | return 0;
82 | }
83 |
84 | @Override
85 | public void writeToParcel(Parcel dest, int flags) {
86 | dest.writeFloatArray(mData);
87 | }
88 |
89 | public void fill(float[] out, int sampleRate) {
90 | int maxFreq = sampleRate / 2;
91 | subFill(out, 0f, 0, EDGE_FREQS[0], maxFreq);
92 | for (int i = 0; i < BAND_COUNT; i++) {
93 | subFill(out, mData[i], EDGE_FREQS[i], EDGE_FREQS[i + 1], maxFreq);
94 | }
95 | subFill(out, 0f, EDGE_FREQS[BAND_COUNT], maxFreq, maxFreq);
96 | }
97 |
98 | private void subFill(float[] out, float setValue, int startFreq, int limitFreq, int maxFreq) {
99 | // This min() applies if the sample rate is below 40kHz.
100 | int limitIndex = Math.min(out.length, limitFreq * out.length / maxFreq);
101 | for (int i = startFreq * out.length / maxFreq; i < limitIndex; i++) {
102 | out[i] = setValue;
103 | }
104 | }
105 |
106 | public boolean sameSpectrum(SpectrumData other) {
107 | if (other == null) {
108 | return false;
109 | }
110 | for (int i = 0; i < BAND_COUNT; i++) {
111 | if (mData[i] != other.mData[i]) {
112 | return false;
113 | }
114 | }
115 | return true;
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/TheBackupAgent.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2016 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.app.backup.BackupAgentHelper;
21 | import android.app.backup.SharedPreferencesBackupHelper;
22 |
23 | // This implements a BackupAgent, not because the data is particularly
24 | // valuable, but because Android 6.0 will randomly kill the service in the
25 | // middle of the night to perform a "fullbackup" if we don't offer an
26 | // alternative (or disable backups entirely.)
27 | public class TheBackupAgent extends BackupAgentHelper {
28 | private static final String PREF_BACKUP_KEY = "pref";
29 |
30 | @Override
31 | public void onCreate() {
32 | addHelper(PREF_BACKUP_KEY, new SharedPreferencesBackupHelper(
33 | this, ChromaDoze.PREF_NAME));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/TrackedPosition.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2013 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | // TrackedPosition keeps track of a single position within
21 | // an ArrayList, even as rows get rearranged by the DSLV.
22 |
23 | public class TrackedPosition {
24 | // Start at -1, so the scratch position can be tracked as well.
25 | private static final int MINVAL = -1;
26 |
27 | // NOWHERE must be larger than any other value, for the math to work.
28 | public static final int NOWHERE = Integer.MAX_VALUE;
29 |
30 | private int mPos = NOWHERE;
31 |
32 | void setPos(int p) {
33 | if (!(MINVAL <= p && p < NOWHERE)) {
34 | throw new IllegalArgumentException("Out of range");
35 | }
36 | mPos = p;
37 | }
38 |
39 | int getPos() {
40 | return mPos;
41 | }
42 |
43 | // Move some item in the list.
44 | // If this position moved to nowhere, throw Deleted.
45 | // Otherwise, return true if this position moved at all.
46 | boolean move(int from, int to) throws Deleted {
47 | // from to result
48 | // ---- -- ------
49 | // = = noop
50 | // = * omg!
51 | // < >= -1
52 | // < < noop
53 | // > <= +1
54 | // > > noop
55 | if (mPos == NOWHERE) {
56 | throw new IllegalStateException();
57 | }
58 | if (from < 0 || to < 0) {
59 | throw new IllegalArgumentException();
60 | }
61 | if (from == mPos) {
62 | if (to != mPos) {
63 | mPos = to;
64 | if (mPos == NOWHERE) {
65 | throw new Deleted();
66 | }
67 | return true;
68 | }
69 | } else if (from < mPos) {
70 | if (to >= mPos) {
71 | mPos -= 1;
72 | if (mPos < MINVAL) {
73 | throw new IllegalStateException();
74 | }
75 | return true;
76 | }
77 | } else if (from > mPos) {
78 | if (to <= mPos) {
79 | mPos += 1;
80 | if (mPos >= NOWHERE) {
81 | throw new IllegalStateException();
82 | }
83 | return true;
84 | }
85 | } else {
86 | throw new RuntimeException();
87 | }
88 | return false;
89 | }
90 |
91 | public static class Deleted extends Exception {
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/UIState.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2011 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | package net.pmarks.chromadoze;
19 |
20 | import android.content.Context;
21 | import android.content.Intent;
22 | import android.content.SharedPreferences;
23 |
24 | import androidx.core.content.ContextCompat;
25 |
26 | import java.util.ArrayList;
27 |
28 | public class UIState {
29 |
30 | private final Context mContext;
31 |
32 | private boolean mLocked = false;
33 | private boolean mLockBusy = false;
34 | private final ArrayList mLockListeners = new ArrayList<>();
35 |
36 | public final TrackedPosition mActivePos = new TrackedPosition();
37 | public PhononMutable mScratchPhonon;
38 | public ArrayList mSavedPhonons;
39 |
40 | public UIState(Context context) {
41 | mContext = context;
42 | }
43 |
44 | private boolean mDirty = false;
45 | private boolean mAutoPlay;
46 | private boolean mIgnoreAudioFocus;
47 | private boolean mVolumeLimitEnabled;
48 | private int mVolumeLimit;
49 | public static final int MAX_VOLUME = 100;
50 |
51 | public void saveState(SharedPreferences.Editor pref) {
52 | pref.putBoolean("locked", mLocked);
53 | pref.putBoolean("autoPlay", mAutoPlay);
54 | pref.putBoolean("ignoreAudioFocus", mIgnoreAudioFocus);
55 | pref.putInt("volumeLimit", getVolumeLimit());
56 | pref.putString("phononS", mScratchPhonon.toJSON());
57 | for (int i = 0; i < mSavedPhonons.size(); i++) {
58 | pref.putString("phonon" + i, mSavedPhonons.get(i).toJSON());
59 | mSavedPhonons.get(i);
60 | }
61 | pref.putInt("activePhonon", mActivePos.getPos());
62 | }
63 |
64 | public void loadState(SharedPreferences pref) {
65 | mLocked = pref.getBoolean("locked", false);
66 | setAutoPlay(pref.getBoolean("autoPlay", false), false);
67 | setIgnoreAudioFocus(pref.getBoolean("ignoreAudioFocus", false));
68 | setVolumeLimit(pref.getInt("volumeLimit", MAX_VOLUME));
69 | setVolumeLimitEnabled(mVolumeLimit != MAX_VOLUME);
70 |
71 | // Load the scratch phonon.
72 | mScratchPhonon = new PhononMutable();
73 | if (mScratchPhonon.loadFromJSON(pref.getString("phononS", null))) {
74 | } else if (mScratchPhonon.loadFromLegacyPrefs(pref)) {
75 | } else {
76 | mScratchPhonon.resetToDefault();
77 | }
78 |
79 | // Load the saved phonons.
80 | mSavedPhonons = new ArrayList<>();
81 | for (int i = 0; i < TrackedPosition.NOWHERE; i++) {
82 | PhononMutable phm = new PhononMutable();
83 | if (!phm.loadFromJSON(pref.getString("phonon" + i, null))) {
84 | break;
85 | }
86 | mSavedPhonons.add(phm);
87 | }
88 |
89 | // Load the currently-selected phonon.
90 | final int active = pref.getInt("activePhonon", -1);
91 | mActivePos.setPos(-1 <= active && active < mSavedPhonons.size() ?
92 | active : -1);
93 | }
94 |
95 | public void addLockListener(LockListener l) {
96 | mLockListeners.add(l);
97 | }
98 |
99 | public void removeLockListener(LockListener l) {
100 | if (!mLockListeners.remove(l)) {
101 | throw new IllegalStateException();
102 | }
103 | }
104 |
105 | private void notifyLockListeners(LockListener.LockEvent e) {
106 | for (LockListener l : mLockListeners) {
107 | l.onLockStateChange(e);
108 | }
109 | }
110 |
111 | public void sendToService() {
112 | Intent intent = new Intent(mContext, NoiseService.class);
113 | getPhonon().writeIntent(intent);
114 | intent.putExtra("volumeLimit", (float) getVolumeLimit() / MAX_VOLUME);
115 | intent.putExtra("ignoreAudioFocus", mIgnoreAudioFocus);
116 | ContextCompat.startForegroundService(mContext, intent);
117 | mDirty = false;
118 | }
119 |
120 | public boolean sendIfDirty() {
121 | if (mDirty || (mActivePos.getPos() == -1 && mScratchPhonon.isDirty())) {
122 | sendToService();
123 | return true;
124 | }
125 | return false;
126 | }
127 |
128 | public void toggleLocked() {
129 | mLocked = !mLocked;
130 | if (!mLocked) {
131 | mLockBusy = false;
132 | }
133 | notifyLockListeners(LockListener.LockEvent.TOGGLE);
134 | }
135 |
136 | public boolean getLocked() {
137 | return mLocked;
138 | }
139 |
140 | public void setLockBusy(boolean busy) {
141 | if (!mLocked) throw new AssertionError("Expected mLocked");
142 | if (mLockBusy != busy) {
143 | mLockBusy = busy;
144 | notifyLockListeners(LockListener.LockEvent.BUSY);
145 | }
146 | }
147 |
148 | public boolean getLockBusy() {
149 | return mLockBusy;
150 | }
151 |
152 | public Phonon getPhonon() {
153 | if (mActivePos.getPos() == -1) {
154 | return mScratchPhonon;
155 | }
156 | return mSavedPhonons.get(mActivePos.getPos());
157 | }
158 |
159 | public PhononMutable getPhononMutable() {
160 | if (mActivePos.getPos() != -1) {
161 | mScratchPhonon = mSavedPhonons.get(mActivePos.getPos()).makeMutableCopy();
162 | mActivePos.setPos(-1);
163 | }
164 | return mScratchPhonon;
165 | }
166 |
167 | // -1 or 0..n
168 | public void setActivePhonon(int index) {
169 | if (!(-1 <= index && index < mSavedPhonons.size())) {
170 | throw new ArrayIndexOutOfBoundsException();
171 | }
172 | mActivePos.setPos(index);
173 | sendToService();
174 | }
175 |
176 | // This interface is for receiving a callback when the state
177 | // of the Input Lock has changed.
178 | public interface LockListener {
179 | enum LockEvent {TOGGLE, BUSY}
180 |
181 | void onLockStateChange(LockEvent e);
182 | }
183 |
184 | public void setAutoPlay(boolean enabled, boolean fromUser) {
185 | mAutoPlay = enabled;
186 | if (fromUser) {
187 | // Demonstrate AutoPlay by acting like the Play/Stop button.
188 | if (enabled) {
189 | sendToService();
190 | } else {
191 | NoiseService.stopNow(mContext, R.string.stop_reason_autoplay);
192 | }
193 | }
194 | }
195 |
196 | public boolean getAutoPlay() {
197 | return mAutoPlay;
198 | }
199 |
200 | public void setIgnoreAudioFocus(boolean enabled) {
201 | if (mIgnoreAudioFocus == enabled) {
202 | return;
203 | }
204 | mIgnoreAudioFocus = enabled;
205 | mDirty = true;
206 | }
207 |
208 | public boolean getIgnoreAudioFocus() {
209 | return mIgnoreAudioFocus;
210 | }
211 |
212 | public void setVolumeLimitEnabled(boolean enabled) {
213 | if (mVolumeLimitEnabled == enabled) {
214 | return;
215 | }
216 | mVolumeLimitEnabled = enabled;
217 | if (mVolumeLimit != MAX_VOLUME) {
218 | mDirty = true;
219 | }
220 | }
221 |
222 | public void setVolumeLimit(int limit) {
223 | if (limit < 0) {
224 | limit = 0;
225 | } else if (limit > MAX_VOLUME) {
226 | limit = MAX_VOLUME;
227 | }
228 | if (mVolumeLimit == limit) {
229 | return;
230 | }
231 | mVolumeLimit = limit;
232 | if (mVolumeLimitEnabled) {
233 | mDirty = true;
234 | }
235 | }
236 |
237 | public boolean getVolumeLimitEnabled() {
238 | return mVolumeLimitEnabled;
239 | }
240 |
241 | public int getVolumeLimit() {
242 | return mVolumeLimitEnabled ? mVolumeLimit : MAX_VOLUME;
243 | }
244 | }
245 |
--------------------------------------------------------------------------------
/app/src/main/java/net/pmarks/chromadoze/XORShiftRandom.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) 2011 Paul Marks http://www.pmarks.net/
2 | //
3 | // This file is part of Chroma Doze.
4 | //
5 | // Chroma Doze is free software: you can redistribute it and/or modify
6 | // it under the terms of the GNU General Public License as published by
7 | // the Free Software Foundation, either version 3 of the License, or
8 | // (at your option) any later version.
9 | //
10 | // Chroma Doze is distributed in the hope that it will be useful,
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | // GNU General Public License for more details.
14 | //
15 | // You should have received a copy of the GNU General Public License
16 | // along with Chroma Doze. If not, see .
17 |
18 | // XORShift is supposedly better and faster than java.util.Random.
19 | // This algorithm is from:
20 | // http://www.javamex.com/tutorials/random_numbers/xorshift.shtml
21 |
22 | package net.pmarks.chromadoze;
23 |
24 | class XORShiftRandom {
25 | private long mState = System.nanoTime();
26 |
27 | public long nextLong() {
28 | long x = mState;
29 | x ^= (x << 21);
30 | x ^= (x >>> 35);
31 | x ^= (x << 4);
32 | mState = x;
33 | return x;
34 | }
35 |
36 | // Get a random number from [0, limit), for small values of limit.
37 | public int nextInt(int limit) {
38 | return ((int) nextLong() & 0x7FFFFFFF) % limit;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/org/jtransforms/dct/FloatDCT_1D.java:
--------------------------------------------------------------------------------
1 | /* ***** BEGIN LICENSE BLOCK *****
2 | * JTransforms
3 | * Copyright (c) 2007 onward, Piotr Wendykier
4 | * All rights reserved.
5 | *
6 | * Redistribution and use in source and binary forms, with or without
7 | * modification, are permitted provided that the following conditions are met:
8 | *
9 | * 1. Redistributions of source code must retain the above copyright notice, this
10 | * list of conditions and the following disclaimer.
11 | * 2. Redistributions in binary form must reproduce the above copyright notice,
12 | * this list of conditions and the following disclaimer in the documentation
13 | * and/or other materials provided with the distribution.
14 | *
15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
19 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 | *
26 | * ***** END LICENSE BLOCK ***** */
27 | package org.jtransforms.dct;
28 |
29 | import org.jtransforms.utils.CommonUtils;
30 | import static java.lang.Math.ceil;
31 | import static java.lang.Math.log;
32 | import static java.lang.Math.sqrt;
33 |
34 | // For Chroma Doze, all code unrelated to the IDCT operation has been deleted.
35 |
36 | /**
37 | * Computes 1D Discrete Cosine Transform (DCT) of single precision data. The
38 | * size of data can be an arbitrary number. This is a parallel implementation of
39 | * split-radix and mixed-radix algorithms optimized for SMP systems.
40 | *
41 | * Part of the code is derived from General Purpose FFT Package written by Takuya Ooura
42 | * (http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html)
43 | *
44 | * @author Piotr Wendykier (piotr.wendykier@gmail.com)
45 | */
46 | public class FloatDCT_1D
47 | {
48 |
49 | private int n;
50 |
51 | private int[] ip;
52 |
53 | private float[] w;
54 |
55 | private int nw;
56 |
57 | private int nc;
58 |
59 | private boolean isPowerOfTwo = false;
60 |
61 | /**
62 | * Creates new instance of FloatDCT_1D.
63 | *
64 | * @param n size of data
65 | *
66 | */
67 | public FloatDCT_1D(long n)
68 | {
69 | if (n < 1) {
70 | throw new IllegalArgumentException("n must be greater than 0");
71 | }
72 |
73 | this.n = (int) n;
74 | if (true) {
75 | if (n > (1 << 28)) {
76 | throw new IllegalArgumentException("n must be smaller or equal to " + (1 << 28) + " when useLargeArrays argument is set to false");
77 | }
78 | if (CommonUtils.isPowerOf2(n)) {
79 | this.isPowerOfTwo = true;
80 | this.ip = new int[(int) ceil(2 + (1 << (int) (log(n / 2 + 0.5) / log(2)) / 2))];
81 | this.w = new float[this.n * 5 / 4];
82 | nw = ip[0];
83 | if (n > (nw << 2)) {
84 | nw = this.n >> 2;
85 | CommonUtils.makewt(nw, ip, w);
86 | }
87 | nc = ip[1];
88 | if (n > nc) {
89 | nc = this.n;
90 | CommonUtils.makect(nc, w, nw, ip);
91 | }
92 | } else {
93 | throw new IllegalStateException();
94 | }
95 | }
96 | }
97 |
98 | /**
99 | * Computes 1D inverse DCT (DCT-III) leaving the result in a
.
100 | *
101 | * @param a
102 | * data to transform
103 | * @param scale
104 | * if true then scaling is performed
105 | */
106 | public void inverse(float[] a, boolean scale)
107 | {
108 | inverse(a, 0, scale);
109 | }
110 |
111 | /**
112 | * Computes 1D inverse DCT (DCT-III) leaving the result in a
.
113 | *
114 | * @param a
115 | * data to transform
116 | * @param offa
117 | * index of the first element in array a
118 | * @param scale
119 | * if true then scaling is performed
120 | */
121 | public void inverse(final float[] a, final int offa, boolean scale)
122 | {
123 | if (n == 1)
124 | return;
125 | if (isPowerOfTwo) {
126 | float xr;
127 | if (scale) {
128 | CommonUtils.scale(n, (float) sqrt(2.0 / n), a, offa, false);
129 | a[offa] = a[offa] / (float) sqrt(2.0);
130 | }
131 | CommonUtils.dctsub(n, a, offa, nc, w, nw);
132 | if (n > 4) {
133 | CommonUtils.cftfsub(n, a, offa, ip, nw, w);
134 | rftfsub(n, a, offa, nc, w, nw);
135 | } else if (n == 4) {
136 | CommonUtils.cftfsub(n, a, offa, ip, nw, w);
137 | }
138 | xr = a[offa] - a[offa + 1];
139 | a[offa] += a[offa + 1];
140 | for (int j = 2; j < n; j += 2) {
141 | a[offa + j - 1] = a[offa + j] - a[offa + j + 1];
142 | a[offa + j] += a[offa + j + 1];
143 | }
144 | a[offa + n - 1] = xr;
145 | } else {
146 | throw new IllegalStateException();
147 | }
148 | }
149 |
150 | private static void rftfsub(int n, float[] a, int offa, int nc, float[] c, int startc)
151 | {
152 | int k, kk, ks, m;
153 | float wkr, wki, xr, xi, yr, yi;
154 | int idx1, idx2;
155 | m = n >> 1;
156 | ks = 2 * nc / m;
157 | kk = 0;
158 | for (int j = 2; j < m; j += 2) {
159 | k = n - j;
160 | kk += ks;
161 | wkr = 0.5f - c[startc + nc - kk];
162 | wki = c[startc + kk];
163 | idx1 = offa + j;
164 | idx2 = offa + k;
165 | xr = a[idx1] - a[idx2];
166 | xi = a[idx1 + 1] + a[idx2 + 1];
167 | yr = wkr * xr - wki * xi;
168 | yi = wkr * xi + wki * xr;
169 | a[idx1] -= yr;
170 | a[idx1 + 1] -= yi;
171 | a[idx2] += yr;
172 | a[idx2 + 1] -= yi;
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi-v11/ic_stat_bars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-hdpi-v11/ic_stat_bars.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi-v9/ic_stat_bars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-hdpi-v9/ic_stat_bars.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/action_lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-hdpi/action_lock.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/action_unlock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-hdpi/action_unlock.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/av_play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-hdpi/av_play.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/av_stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-hdpi/av_stop.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-hdpi/btn_default_disabled_focused_holo_dark.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-hdpi/btn_default_disabled_holo_dark.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/btn_default_focused_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-hdpi/btn_default_focused_holo_dark.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/btn_default_normal_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-hdpi/btn_default_normal_holo_dark.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/btn_default_pressed_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-hdpi/btn_default_pressed_holo_dark.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/grip_dots.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-hdpi/grip_dots.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_menu_save_disabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-hdpi/ic_menu_save_disabled.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_menu_save_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-hdpi/ic_menu_save_normal.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_stat_bars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-hdpi/ic_stat_bars.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-ldpi-v11/ic_stat_bars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-ldpi-v11/ic_stat_bars.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-ldpi-v9/ic_stat_bars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-ldpi-v9/ic_stat_bars.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-ldpi/wave_amplitude.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-ldpi/wave_amplitude.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-ldpi/wave_period.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-ldpi/wave_period.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi-v11/ic_stat_bars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-mdpi-v11/ic_stat_bars.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi-v9/ic_stat_bars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-mdpi-v9/ic_stat_bars.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-mdpi/btn_default_disabled_focused_holo_dark.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-mdpi/btn_default_disabled_holo_dark.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/btn_default_focused_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-mdpi/btn_default_focused_holo_dark.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/btn_default_normal_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-mdpi/btn_default_normal_holo_dark.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/btn_default_pressed_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-mdpi/btn_default_pressed_holo_dark.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_stat_bars.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-mdpi/ic_stat_bars.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/toolbar_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-mdpi/toolbar_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/btn_default_disabled_focused_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-xhdpi/btn_default_disabled_focused_holo_dark.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/btn_default_disabled_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-xhdpi/btn_default_disabled_holo_dark.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/btn_default_focused_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-xhdpi/btn_default_focused_holo_dark.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/btn_default_normal_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-xhdpi/btn_default_normal_holo_dark.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/btn_default_pressed_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-xhdpi/btn_default_pressed_holo_dark.9.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/toolbar_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable-xhdpi/toolbar_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/btn_default_holo_dark.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_save.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/spectrum.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/drawable/spectrum.png
--------------------------------------------------------------------------------
/app/src/main/res/layout-v11/notification_with_stop_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
19 |
20 |
25 |
26 |
32 |
33 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/about_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
14 |
15 |
20 |
21 |
27 |
28 |
34 |
35 |
40 |
41 |
46 |
47 |
48 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
17 |
18 |
19 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/main_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
14 |
15 |
20 |
21 |
22 |
23 |
30 |
31 |
32 |
33 |
34 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/memory_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/memory_list_divider.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/memory_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
19 |
20 |
21 |
22 |
23 |
24 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/memory_list_item_common.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/memory_list_item_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/options_fragment.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
20 |
21 |
25 |
26 |
29 |
30 |
36 |
37 |
43 |
44 |
50 |
51 |
52 |
55 |
56 |
60 |
61 |
67 |
68 |
69 |
72 |
73 |
79 |
80 |
87 |
88 |
94 |
95 |
96 |
97 |
101 |
102 |
109 |
110 |
117 |
118 |
125 |
126 |
133 |
134 |
138 |
139 |
140 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/spinner_title.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/chromadoze_icon.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/chromadoze_icon_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/chromadoze_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/mipmap-hdpi/chromadoze_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/chromadoze_icon_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/mipmap-hdpi/chromadoze_icon_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/chromadoze_icon_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/mipmap-hdpi/chromadoze_icon_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/chromadoze_icon_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/mipmap-hdpi/chromadoze_icon_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/chromadoze_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/mipmap-mdpi/chromadoze_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/chromadoze_icon_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/mipmap-mdpi/chromadoze_icon_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/chromadoze_icon_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/mipmap-mdpi/chromadoze_icon_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/chromadoze_icon_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/mipmap-mdpi/chromadoze_icon_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/chromadoze_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/mipmap-xhdpi/chromadoze_icon.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/chromadoze_icon_background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/mipmap-xhdpi/chromadoze_icon_background.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/chromadoze_icon_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/mipmap-xhdpi/chromadoze_icon_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/chromadoze_icon_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/app/src/main/res/mipmap-xhdpi/chromadoze_icon_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #000000
5 | #201E24
6 | #33b5e5
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dslv_attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Chroma Doze
4 | Chroma Doze is running
5 | About
6 | Options
7 | Generating IDCT blocks:
8 | Play/Stop
9 | Lock/Unlock Input
10 | Memory
11 | (unsaved)
12 | Stopped by auto-play option
13 | Lost audio focus
14 | Stopped from notification bar
15 | Stopped from toolbar
16 | Stopped by media button
17 | Exited while silent
18 | Stopped mysteriously
19 | Restarted by the OS
20 | Default
21 | This notification appears when ChromaDoze is generating noise
22 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | mavenCentral()
5 | google()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:8.3.1'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | mavenCentral()
15 | google()
16 | }
17 | tasks.withType(JavaCompile) {
18 | options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | android.useAndroidX=true
2 | android.enableJetifier=false
3 | android.nonTransitiveRClass=false
4 | android.nonFinalResIds=false
5 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | #
4 | # Copyright © 2015-2021 the original authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | #
21 | # Gradle start up script for POSIX generated by Gradle.
22 | #
23 | # Important for running:
24 | #
25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26 | # noncompliant, but you have some other compliant shell such as ksh or
27 | # bash, then to run this script, type that shell name before the whole
28 | # command line, like:
29 | #
30 | # ksh Gradle
31 | #
32 | # Busybox and similar reduced shells will NOT work, because this script
33 | # requires all of these POSIX shell features:
34 | # * functions;
35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37 | # * compound commands having a testable exit status, especially «case»;
38 | # * various built-in commands including «command», «set», and «ulimit».
39 | #
40 | # Important for patching:
41 | #
42 | # (2) This script targets any POSIX shell, so it avoids extensions provided
43 | # by Bash, Ksh, etc; in particular arrays are avoided.
44 | #
45 | # The "traditional" practice of packing multiple parameters into a
46 | # space-separated string is a well documented source of bugs and security
47 | # problems, so this is (mostly) avoided, by progressively accumulating
48 | # options in "$@", and eventually passing that to Java.
49 | #
50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52 | # see the in-line comments for details.
53 | #
54 | # There are tweaks for specific operating systems such as AIX, CygWin,
55 | # Darwin, MinGW, and NonStop.
56 | #
57 | # (3) This script is generated from the Groovy template
58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59 | # within the Gradle project.
60 | #
61 | # You can find Gradle at https://github.com/gradle/gradle/.
62 | #
63 | ##############################################################################
64 |
65 | # Attempt to set APP_HOME
66 |
67 | # Resolve links: $0 may be a link
68 | app_path=$0
69 |
70 | # Need this for daisy-chained symlinks.
71 | while
72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
73 | [ -h "$app_path" ]
74 | do
75 | ls=$( ls -ld "$app_path" )
76 | link=${ls#*' -> '}
77 | case $link in #(
78 | /*) app_path=$link ;; #(
79 | *) app_path=$APP_HOME$link ;;
80 | esac
81 | done
82 |
83 | # This is normally unused
84 | # shellcheck disable=SC2034
85 | APP_BASE_NAME=${0##*/}
86 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
87 | APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
88 |
89 | # Use the maximum available, or set MAX_FD != -1 to use that value.
90 | MAX_FD=maximum
91 |
92 | warn () {
93 | echo "$*"
94 | } >&2
95 |
96 | die () {
97 | echo
98 | echo "$*"
99 | echo
100 | exit 1
101 | } >&2
102 |
103 | # OS specific support (must be 'true' or 'false').
104 | cygwin=false
105 | msys=false
106 | darwin=false
107 | nonstop=false
108 | case "$( uname )" in #(
109 | CYGWIN* ) cygwin=true ;; #(
110 | Darwin* ) darwin=true ;; #(
111 | MSYS* | MINGW* ) msys=true ;; #(
112 | NONSTOP* ) nonstop=true ;;
113 | esac
114 |
115 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
116 |
117 |
118 | # Determine the Java command to use to start the JVM.
119 | if [ -n "$JAVA_HOME" ] ; then
120 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
121 | # IBM's JDK on AIX uses strange locations for the executables
122 | JAVACMD=$JAVA_HOME/jre/sh/java
123 | else
124 | JAVACMD=$JAVA_HOME/bin/java
125 | fi
126 | if [ ! -x "$JAVACMD" ] ; then
127 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
128 |
129 | Please set the JAVA_HOME variable in your environment to match the
130 | location of your Java installation."
131 | fi
132 | else
133 | JAVACMD=java
134 | if ! command -v java >/dev/null 2>&1
135 | then
136 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137 |
138 | Please set the JAVA_HOME variable in your environment to match the
139 | location of your Java installation."
140 | fi
141 | fi
142 |
143 | # Increase the maximum file descriptors if we can.
144 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
145 | case $MAX_FD in #(
146 | max*)
147 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
148 | # shellcheck disable=SC2039,SC3045
149 | MAX_FD=$( ulimit -H -n ) ||
150 | warn "Could not query maximum file descriptor limit"
151 | esac
152 | case $MAX_FD in #(
153 | '' | soft) :;; #(
154 | *)
155 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
156 | # shellcheck disable=SC2039,SC3045
157 | ulimit -n "$MAX_FD" ||
158 | warn "Could not set maximum file descriptor limit to $MAX_FD"
159 | esac
160 | fi
161 |
162 | # Collect all arguments for the java command, stacking in reverse order:
163 | # * args from the command line
164 | # * the main class name
165 | # * -classpath
166 | # * -D...appname settings
167 | # * --module-path (only if needed)
168 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
169 |
170 | # For Cygwin or MSYS, switch paths to Windows format before running java
171 | if "$cygwin" || "$msys" ; then
172 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
173 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
174 |
175 | JAVACMD=$( cygpath --unix "$JAVACMD" )
176 |
177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
178 | for arg do
179 | if
180 | case $arg in #(
181 | -*) false ;; # don't mess with options #(
182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
183 | [ -e "$t" ] ;; #(
184 | *) false ;;
185 | esac
186 | then
187 | arg=$( cygpath --path --ignore --mixed "$arg" )
188 | fi
189 | # Roll the args list around exactly as many times as the number of
190 | # args, so each arg winds up back in the position where it started, but
191 | # possibly modified.
192 | #
193 | # NB: a `for` loop captures its iteration list before it begins, so
194 | # changing the positional parameters here affects neither the number of
195 | # iterations, nor the values presented in `arg`.
196 | shift # remove old arg
197 | set -- "$@" "$arg" # push replacement arg
198 | done
199 | fi
200 |
201 |
202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
204 |
205 | # Collect all arguments for the java command:
206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
207 | # and any embedded shellness will be escaped.
208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
209 | # treated as '${Hostname}' itself on the command line.
210 |
211 | set -- \
212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \
213 | -classpath "$CLASSPATH" \
214 | org.gradle.wrapper.GradleWrapperMain \
215 | "$@"
216 |
217 | # Stop when "xargs" is not available.
218 | if ! command -v xargs >/dev/null 2>&1
219 | then
220 | die "xargs is not available"
221 | fi
222 |
223 | # Use "xargs" to parse quoted args.
224 | #
225 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
226 | #
227 | # In Bash we could simply go:
228 | #
229 | # readarray ARGS < <( xargs -n1 <<<"$var" ) &&
230 | # set -- "${ARGS[@]}" "$@"
231 | #
232 | # but POSIX shell has neither arrays nor command substitution, so instead we
233 | # post-process each arg (as a line of input to sed) to backslash-escape any
234 | # character that might be a shell metacharacter, then use eval to reverse
235 | # that process (while maintaining the separation between arguments), and wrap
236 | # the whole thing up as a single "set" statement.
237 | #
238 | # This will of course break if any of these variables contains a newline or
239 | # an unmatched quote.
240 | #
241 |
242 | eval "set -- $(
243 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
244 | xargs -n1 |
245 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
246 | tr '\n' ' '
247 | )" '"$@"'
248 |
249 | exec "$JAVACMD" "$@"
250 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/misc/bars.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/misc/bars.xcf
--------------------------------------------------------------------------------
/misc/chromadoze-feature-1024x500.xcf.bz2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/misc/chromadoze-feature-1024x500.xcf.bz2
--------------------------------------------------------------------------------
/misc/chromadoze-icon-48.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/misc/chromadoze-icon-48.xcf
--------------------------------------------------------------------------------
/misc/chromadoze-icon-512.xcf.bz2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/misc/chromadoze-icon-512.xcf.bz2
--------------------------------------------------------------------------------
/misc/chromadoze-icon-96.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/misc/chromadoze-icon-96.xcf
--------------------------------------------------------------------------------
/misc/chromadoze-icon-plain-48.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/misc/chromadoze-icon-plain-48.xcf
--------------------------------------------------------------------------------
/misc/importing-dslv.txt:
--------------------------------------------------------------------------------
1 | Here's how I merged DragSortListView into the Chroma Doze tree:
2 |
3 | - Download from here:
4 | https://github.com/bauerca/drag-sort-listview
5 |
6 | - Copy these paths:
7 | + library/res/values/dslv_attrs.xml
8 | + library/res/values/ids.xml
9 | + library/src/...
10 |
11 | - Add this line to to DragSortListView.java:
12 | import net.pmarks.chromadoze.R;
13 |
--------------------------------------------------------------------------------
/misc/privacy_policy.txt:
--------------------------------------------------------------------------------
1 | Privacy Policy
2 |
3 | Chroma Doze does not handle personal information.
4 |
--------------------------------------------------------------------------------
/misc/readme-android-studio.txt:
--------------------------------------------------------------------------------
1 | Importing Chroma Doze into Android Studio should hopefully be trivial:
2 |
3 | - Launch Android Studio.
4 | - Click "Import Project (Eclipse ADT, Gradle, etc.)"
5 | - Browse for the root of the repository.
6 |
--------------------------------------------------------------------------------
/misc/screenshot1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/misc/screenshot1.png
--------------------------------------------------------------------------------
/misc/screenshot2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/misc/screenshot2.png
--------------------------------------------------------------------------------
/misc/screenshot3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pmarks-net/chromadoze/d97acd88c21de7d4136c4215245d02fa6631af72/misc/screenshot3.png
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
--------------------------------------------------------------------------------