Skip to content
This repository was archived by the owner on Nov 25, 2019. It is now read-only.

Commit 0be0fa6

Browse files
committed
Initial commit
1 parent cd51cb6 commit 0be0fa6

File tree

106 files changed

+18877
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+18877
-0
lines changed

app/build.gradle

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
apply plugin: 'com.android.application'
2+
3+
apply plugin: 'kotlin-android'
4+
5+
apply plugin: 'kotlin-android-extensions'
6+
7+
android {
8+
compileSdkVersion 27
9+
defaultConfig {
10+
applicationId "com.n0n3m4.reducenative"
11+
minSdkVersion 14
12+
targetSdkVersion 28
13+
versionCode 1
14+
versionName "1.0"
15+
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
16+
}
17+
buildTypes {
18+
release {
19+
minifyEnabled false
20+
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21+
}
22+
}
23+
signingConfigs {
24+
release {
25+
keyAlias 'reduce_key'
26+
keyPassword 'password'
27+
storePassword 'password'
28+
storeFile file('../keystore.jks')
29+
}
30+
}
31+
}
32+
33+
dependencies {
34+
implementation fileTree(dir: 'libs', include: ['*.jar'])
35+
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
36+
implementation 'com.android.support:appcompat-v7:27.1.1'
37+
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
38+
implementation 'com.android.support:design:27.1.1'
39+
testImplementation 'junit:junit:4.12'
40+
androidTestImplementation 'com.android.support.test:runner:1.0.2'
41+
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
42+
compile project(path: ':reducemathview')
43+
}

app/proguard-rules.pro

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile

app/src/main/AndroidManifest.xml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="com.n0n3m4.reducenative">
4+
5+
<application
6+
android:allowBackup="true"
7+
android:icon="@mipmap/ic_launcher"
8+
android:label="@string/app_name"
9+
android:supportsRtl="true"
10+
android:theme="@style/AppTheme"
11+
android:extractNativeLibs="true">
12+
<activity
13+
android:name=".MainActivity"
14+
android:label="@string/app_name"
15+
android:theme="@style/AppTheme.NoActionBar"
16+
android:configChanges="keyboardHidden|orientation|screenSize">
17+
<intent-filter>
18+
<action android:name="android.intent.action.MAIN" />
19+
20+
<category android:name="android.intent.category.LAUNCHER" />
21+
</intent-filter>
22+
</activity>
23+
</application>
24+
25+
</manifest>
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
package com.n0n3m4.reducenative
2+
3+
/*
4+
The author of this code is n0n3m4, this code is public domain
5+
*/
6+
7+
import android.os.Bundle
8+
import android.os.Handler
9+
import android.support.annotation.UiThread
10+
import android.support.annotation.WorkerThread
11+
import android.support.v7.app.AppCompatActivity
12+
import android.support.v7.widget.AppCompatTextView
13+
import android.support.v7.widget.LinearLayoutManager
14+
import android.support.v7.widget.ListPopupWindow
15+
import android.support.v7.widget.RecyclerView
16+
import android.util.Log
17+
import android.view.*
18+
import android.view.inputmethod.EditorInfo
19+
import android.widget.*
20+
import com.n0n3m4.mathlib.MathView
21+
22+
import kotlinx.android.synthetic.main.activity_main.*
23+
import kotlinx.android.synthetic.main.content_main.*
24+
import java.io.*
25+
import android.widget.FrameLayout
26+
import android.view.View.MeasureSpec
27+
import android.view.ViewGroup
28+
29+
30+
31+
class MainActivity : AppCompatActivity()
32+
{
33+
34+
val TYPE_LATEX = 0
35+
val TYPE_USER = 1
36+
val TYPE_REDUCETEXT = 2
37+
38+
val handler = Handler()
39+
40+
val TAG = "ReduceNative"
41+
var proc: Process? = null;
42+
var writer: PrintWriter? = null;
43+
val reducePath: File
44+
get() = File(applicationInfo.nativeLibraryDir,"libreduce.so");
45+
val reduceImgPath: File
46+
get() = File(applicationInfo.nativeLibraryDir,"libreduce.img.so");
47+
48+
@UiThread
49+
fun datasetAppend(el: ListElement)
50+
{
51+
dataset.add(el)
52+
reduceOutput.adapter.notifyItemInserted(dataset.size-1)
53+
scrollDown()
54+
}
55+
56+
var inputsPosted = 0
57+
@UiThread
58+
fun postInput(input:String): Boolean
59+
{
60+
if ((writer == null) || (input == ""))
61+
return false
62+
else
63+
{
64+
inputsPosted++
65+
datasetAppend(ListElement(input,TYPE_USER,inputsPosted))
66+
writer!!.println(input + (if (!input.endsWith(';')) ";" else ""))
67+
return true;
68+
}
69+
}
70+
71+
@UiThread
72+
fun sendCurrentCommand()
73+
{
74+
if (postInput(inputCommand.text.toString()))
75+
inputCommand.setText("")
76+
}
77+
78+
@WorkerThread
79+
fun readerThreadProc(br:BufferedReader)
80+
{
81+
var _s:String? = null
82+
83+
// fancy!-out!-header
84+
val LINESTART = "\u0002latex:\\black\$\\displaystyle "
85+
// fancy!-out!-trailer
86+
val LINEEND = "\$\u0005"
87+
88+
while ({_s=br.readLine();_s}()!=null)
89+
{
90+
val s=_s!!
91+
Log.v(TAG,s)
92+
if (s.startsWith(LINESTART) && s.endsWith(LINEEND))
93+
{
94+
// This is a Latex output
95+
val outline = s.substring(LINESTART.length,s.length-LINEEND.length)
96+
runOnUiThread { datasetAppend(ListElement(outline,TYPE_LATEX,0)) }
97+
}
98+
else
99+
{
100+
// We skip empty lines
101+
if (s!="")
102+
runOnUiThread { datasetAppend(ListElement(s,TYPE_REDUCETEXT,0)) }
103+
}
104+
}
105+
}
106+
107+
@UiThread
108+
fun scrollDown()
109+
{
110+
reduceOutput.smoothScrollToPosition(reduceOutput.adapter.getItemCount())
111+
}
112+
113+
@WorkerThread
114+
fun startReduce()
115+
{
116+
proc = ProcessBuilder(reducePath.absolutePath,"-i",reduceImgPath.absolutePath,"--texmacs").directory(filesDir).start()
117+
val reader = BufferedReader(InputStreamReader(proc!!.inputStream))
118+
writer = PrintWriter(proc!!.outputStream,true)
119+
// Skip empty line
120+
Log.v(TAG,reader.readLine())
121+
// Set line length to be 100M, so the line breaking occurs after our app dies completely
122+
writer!!.println("linelength 100000000;")
123+
// Skip output of linelength
124+
Log.v(TAG,reader.readLine())
125+
// Now start the reader
126+
Thread{ readerThreadProc(reader)}.start()
127+
}
128+
129+
fun stopReduce()
130+
{
131+
writer!!.println("quit;")
132+
}
133+
134+
class ListElement(val value: String, val type: Int, val linenum: Int)
135+
136+
inner class ReduceOutputAdapter(val dataset: ArrayList<ListElement>) : RecyclerView.Adapter<ReduceOutputAdapter.ViewHolder>()
137+
{
138+
inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view)
139+
// Create new views (invoked by the layout manager)
140+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ReduceOutputAdapter.ViewHolder
141+
{
142+
if (viewType == TYPE_LATEX)
143+
{
144+
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.reduceoutput, parent, false))
145+
}
146+
else if (viewType == TYPE_USER)
147+
{
148+
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.reduceinput, parent, false))
149+
}
150+
else
151+
return ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.reduceoutputtext, parent, false))
152+
}
153+
154+
override fun getItemViewType(position: Int): Int
155+
{
156+
return dataset[position].type;
157+
}
158+
159+
// Replace the contents of a view (invoked by the layout manager)
160+
override fun onBindViewHolder(holder: ViewHolder, position: Int)
161+
{
162+
if (getItemViewType(position) == TYPE_LATEX)
163+
{
164+
val mathview = holder.view.findViewById<MathView>(R.id.mathview)
165+
if (position == dataset.size - 1)
166+
mathview.listenOnSize {
167+
if (position != dataset.size - 1)
168+
mathview.delistenOnSize()
169+
else
170+
scrollDown()
171+
}
172+
mathview.setDisplayText( "\$" + dataset[position].value + "\$")
173+
}
174+
else if (getItemViewType(position) == TYPE_USER)
175+
{
176+
(holder.view as LinearLayout).findViewById<AppCompatTextView>(R.id.ri_linenum).text = "${dataset[position].linenum}: "
177+
(holder.view as LinearLayout).findViewById<AppCompatTextView>(R.id.ri_content).text = dataset[position].value
178+
}
179+
else
180+
{
181+
(holder.view as AppCompatTextView).text = dataset[position].value
182+
}
183+
}
184+
185+
// Return the size of your dataset (invoked by the layout manager)
186+
override fun getItemCount() = dataset.size
187+
}
188+
189+
val dataset = ArrayList<ListElement>();
190+
191+
override fun onCreate(savedInstanceState: Bundle?)
192+
{
193+
super.onCreate(savedInstanceState)
194+
setContentView(R.layout.activity_main)
195+
196+
Thread {
197+
startReduce()
198+
}.start()
199+
200+
// Can't use XML due to API levels
201+
reduceOutput.isNestedScrollingEnabled = false
202+
reduceOutput.layoutManager = LinearLayoutManager(this)
203+
reduceOutput.adapter = ReduceOutputAdapter(dataset)
204+
205+
inputCommand.setOnEditorActionListener { _, actionId, _ ->
206+
if (actionId == EditorInfo.IME_ACTION_DONE)
207+
{
208+
sendCurrentCommand();
209+
true;
210+
}
211+
else
212+
{
213+
false;
214+
}
215+
}
216+
217+
setSupportActionBar(toolbar)
218+
219+
send_command.setOnClickListener { view ->
220+
sendCurrentCommand()
221+
}
222+
223+
repeat_command.setOnClickListener { _ ->
224+
val dataz = dataset.filter { x -> x.type == TYPE_USER }.map { x -> x.value }
225+
if (dataz.size == 0)
226+
return@setOnClickListener
227+
val pw = ListPopupWindow(this)
228+
val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, dataz)
229+
pw.width = measureContentWidth(adapter)
230+
pw.anchorView = repeat_command
231+
pw.setAdapter(adapter)
232+
pw.setOnItemClickListener { _, _, position, _ ->
233+
inputCommand.setText(adapter.getItem(position))
234+
pw.dismiss()
235+
}
236+
pw.show()
237+
pw.listView!!.setSelection(dataz.size - 1)
238+
}
239+
}
240+
241+
// https://stackoverflow.com/questions/14200724/listpopupwindow-not-obeying-wrap-content-width-spec
242+
private fun measureContentWidth(listAdapter: ListAdapter): Int
243+
{
244+
var mMeasureParent: ViewGroup? = null
245+
var maxWidth = 0
246+
var itemView: View? = null
247+
var itemType = 0
248+
249+
val widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
250+
val heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
251+
val count = listAdapter.count
252+
for (i in 0 until count)
253+
{
254+
val positionType = listAdapter.getItemViewType(i)
255+
if (positionType != itemType)
256+
{
257+
itemType = positionType
258+
itemView = null
259+
}
260+
if (mMeasureParent == null)
261+
{
262+
mMeasureParent = FrameLayout(this)
263+
}
264+
itemView = listAdapter.getView(i, itemView, mMeasureParent)
265+
itemView!!.measure(widthMeasureSpec, heightMeasureSpec)
266+
val itemWidth = itemView.measuredWidth
267+
if (itemWidth > maxWidth)
268+
{
269+
maxWidth = itemWidth
270+
}
271+
}
272+
return maxWidth
273+
}
274+
275+
override fun onCreateOptionsMenu(menu: Menu): Boolean
276+
{
277+
menuInflater.inflate(R.menu.menu_main, menu)
278+
return true
279+
}
280+
281+
override fun onOptionsItemSelected(item: MenuItem): Boolean
282+
{
283+
return when (item.itemId)
284+
{
285+
else -> super.onOptionsItemSelected(item)
286+
}
287+
}
288+
289+
override fun onDestroy()
290+
{
291+
stopReduce()
292+
super.onDestroy()
293+
}
294+
}
3.66 MB
Binary file not shown.
4.68 MB
Binary file not shown.

0 commit comments

Comments
 (0)