A summary of my Android apps:BuptRoom


Introduction

Write an app to query the school's empty classrooms Pull information from the school's registration website, classify and display it, and add some miscellaneous things After all, it's my first time writing Android, so I want to try everything Download here: BuptRoom repository address: A simple Beiyou self-study room query system It took about 3 weekends to complete the first version, and then I spent about 1 month updating miscellaneous things After that, I spent about 1 month updating miscellaneous things Many things written in an unstandardized manner, and I just looked up and used them temporarily Summarize the experience of writing the App:

Learning content

  • Android basic architecture, components, lifecycle
  • Fragment
  • Java library and library calls
  • Github usage
  • Deploy app
  • Image processing methods
  • A stupid way to pull web content
  • Utilize GitHub third-party libraries
  • Color knowledge
  • Android Material Design
  • Simple optimization
  • Multithreading and Handler

Solved problems

Mainly solved the following problems:

  • Android 6.0 and above versions seem to require dynamic permission verification, and the current version only supports 5.0 and below, used permisson:
1
2
3
4
5
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission>
  • The webpage is a jsp dynamic webpage, which cannot be simply parsed, so I finally used loadurl in webview to execute javascript commands, and need to download the jsoup-1.9.2.jar package and add it to the library file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
final class MyWebViewClient extends WebViewClient {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
public void onPageStarted(WebView view, String url, Bitmap favicon) {
Log.d("WebView","onPageStarted");
super.onPageStarted(view, url, favicon);
}
public void onPageFinished(WebView view, String url) {
Log.d("WebView","onPageFinished ");
view.loadUrl("javascript:window.handler.getContent(document.body.innerHTML);");
super.onPageFinished(view, url);
}
}
  • Write a handler to respond to javascript commands, so that the body content in the html file is obtained in String form
1
2
3
4
5
6
7
8
9
10
final  class JavascriptHandler{
@JavascriptInterface
public void getContent(String htmlContent){
Log.i(Tag,"html content: "+htmlContent);
document= Jsoup.parse(htmlContent);
htmlstring=htmlContent;
content=document.getElementsByTag("body").text();
Toast.makeText(MainActivity.this,"加载完成",Toast.LENGTH_SHORT).show();
}
}
  • After that, string processing, according to the format provided by the registration website
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
Remove commas
String contenttemp=content;
content="";
String[] contentstemp=contenttemp.split(",");
for (String temp:contentstemp){
content=content+temp;
}

Group
contents=content.split(" |:");
String showcontent="";
count=0;
int tsgflag=0;
int cishu=0;
j12.clear();
j34.clear();
j56.clear();
j78.clear();
j9.clear();
j1011.clear();
if (keyword.contains("图书馆")) tsgflag=1;
for (String temp:contents){
if (temp.contains(keyword)){
cishu++;
SaveBuidlingInfo(count,cishu,tsgflag);
}
count++;
}

SaveBuildingInfo is to classify and store classrooms by building, and then classify and store them by time period in j12,j34.....
while (1 == 1) {
if (contents[k].contains("楼") || contents[k].contains("节") || contents[k].contains("图"))
break;
;
switch (c) {
case 1:
j12.add(contents[k]);
break;
case 2:
j34.add(contents[k]);
break;
case 3:
j56.add(contents[k]);
break;
case 4:
j78.add(contents[k]);
break;
case 5:
j9.add(contents[k]);
break;
case 6:
j1011.add(contents[k]);
break;
default:
break;
}
k++;
}
  • The NavigationView is a simple one, with no special design, because there are no multiple interfaces, just use the refresh TextView to fake multiple interfaces

  • Tried the MaterialDesign components, added some things about system time

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
final Calendar c = Calendar.getInstance();
c.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
mYear = String.valueOf(c.get(Calendar.YEAR)); // 获取当前年份
mMonth = String.valueOf(c.get(Calendar.MONTH) + 1);// 获取当前月份
mDay = String.valueOf(c.get(Calendar.DAY_OF_MONTH));// 获取当前月份的日期号码
mWay = String.valueOf(c.get(Calendar.DAY_OF_WEEK));
mHour= c.get(Calendar.HOUR_OF_DAY);
mMinute= c.get(Calendar.MINUTE);

if (mHour>=8&&mHour<10){
nowtime="现在是一二节课";
}else
if (mHour>=10&&mHour<12){
nowtime="现在是三四节课";
}else
if ((mHour==13&&mMinute>=30)||(mHour==14)||(mHour==15&&mMinute<30)){
nowtime="现在是五六节课";
}else
if ((mHour==15&&mMinute>=30)||(mHour==16)||(mHour==17&&mMinute<30)){
nowtime="现在是七八节课";
}else
if ((mHour==17&&mMinute>=30)||(mHour==18&&mMinute<30)){
nowtime="现在是第九节课";
}else
if ((mHour==18&&mMinute>=30)||(mHour==19)||(mHour==20&&mMinute<30)){
nowtime="现在是十、十一节课";
}else
nowtime="现在是休息时间";

if("1".equals(mWay)){
mWay ="天";
daycount=6;
}else if("2".equals(mWay)){
mWay ="一";
daycount=0;
}else if("3".equals(mWay)){
mWay ="二";
daycount=1;
}else if("4".equals(mWay)){
mWay ="三";
daycount=2;
}else if("5".equals(mWay)){
mWay ="四";
daycount=3;
}else if("6".equals(mWay)){
mWay ="五";
daycount=4;
}else if("7".equals(mWay)){
mWay ="六";
daycount=5;
}
Timestring=mYear + "年" + mMonth + "月" + mDay+"日"+"星期"+mWay;

FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "今天是"+Timestring+"\n"+nowtime+" "+interesting[daycount], Snackbar.LENGTH_SHORT)
.setAction("Action", null).show();
}
});

What I learned on GitHub

In addition to trying to use other GitHub libraries, I learned a lot, including color palettes, shake modules, fir update modules, sliding card interfaces, etc. Some GitHub repository links are here

Some of them are directly written in the code, and I forgot the original address....

  • Sensor call for shake

    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    public class ShakeService extends Service {
    public static final String TAG = "ShakeService";
    private SensorManager mSensorManager;
    public boolean flag=false;
    private ShakeBinder shakebinder= new ShakeBinder();
    private String htmlbody="";

    @Override
    public void onCreate() {
    // TODO Auto-generated method stub
    super.onCreate();
    mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
    Log.i(TAG,"Shake Service Create");
    }

    @Override
    public void onDestroy() {
    // TODO Auto-generated method stub
    flag=false;
    super.onDestroy();
    mSensorManager.unregisterListener(mShakeListener);
    }

    @Override
    public void onStart(Intent intent, int startId) {
    // TODO Auto-generated method stub
    super.onStart(intent, startId);
    Log.i(TAG,"Shake Service Start");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    // TODO Auto-generated method stub
    mSensorManager.registerListener(mShakeListener,
    mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
    //SensorManager.SENSOR_DELAY_GAME,
    50 * 1000); //batch every 50 milliseconds
    htmlbody=intent.getStringExtra("htmlbody");

    return super.onStartCommand(intent, flags, startId);
    }

    private final SensorEventListener mShakeListener = new SensorEventListener() {
    private static final float SENSITIVITY = 10;
    private static final int BUFFER = 5;
    private float[] gravity = new float[3];
    private float average = 0;
    private int fill = 0;

    @Override
    public void onAccuracyChanged(Sensor sensor, int acc) {
    }

    public void onSensorChanged(SensorEvent event) {
    final float alpha = 0.8F;

    for (int i = 0; i < 3; i++) {
    gravity[i] = alpha * gravity[i] + (1 - alpha) * event.values[i];
    }

    float x = event.values[0] - gravity[0];
    float y = event.values[1] - gravity[1];
    float z = event.values[2] - gravity[2];

    if (fill <= BUFFER) {
    average += Math.abs(x) + Math.abs(y) + Math.abs(z);
    fill++;
    } else {
    Log.i(TAG, "average:"+average);
    Log.i(TAG, "average / BUFFER:"+(average / BUFFER));
    if (average / BUFFER >= SENSITIVITY) {
    handleShakeAction();//如果达到阈值则处理摇一摇响应
    }
    average = 0;
    fill = 0;
    }
    }
    };

    protected void handleShakeAction() {
    // TODO Auto-generated method stub
    flag=true;
    Toast.makeText(getApplicationContext(), "摇一摇成功", Toast.LENGTH_SHORT).show();
    Intent intent= new Intent();
    intent.putExtra("htmlbody",htmlbody);
    intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
    intent.setClassName(this,"thinkwee.buptroom.ShakeTestActivity");
    startActivity(intent);
    }

    @Override
    public IBinder onBind(Intent intent) {
    // TODO Auto-generated method stub
    return shakebinder;
    }
    class ShakeBinder extends Binder{

    }
    }
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
32
33
34
35
36
37
38
39
40
41
42
43
44
# Independent network pull and multi-threading
- In the previous structure, network pull was integrated in the welcome activity, in order to add a refresh function in the main interface and call the network pull at any time, I wrote the network pull as a separate class, which can be called when needed
- However, in the welcome activity, the welcome animation and network pull are in two separate threads (to prevent the animation from being blocked), so there may be a situation where the welcome animation is completed and the main interface is entered, but the network pull is not completed, and the content pulled cannot be passed to the main interface. The final solution is to set a 2s timeout for the network pull. If it is not pulled, an incorrect parameter is passed to the activity that starts the main interface, prompting a refresh
```Java
webget = new Webget();
webget.init(webView);
HaveNetFlag = webget.WebInit();

new Handler().postDelayed(new Runnable() {
public void run() {
//execute the task
ImageView img = (ImageView) findViewById(R.id.welcomeimg);
Animation animation = AnimationUtils.loadAnimation(WelcomeActivity.this, R.anim.enlarge);
animation.setFillAfter(true);
img.startAnimation(animation);
}
}, 50);

new Handler().postDelayed(new Runnable() {
public void run() {
//execute the task
WrongNet = webget.getWrongnet();
HaveNetFlag = webget.getHaveNetFlag();
htmlbody = webget.getHtmlbody();
Log.i("welcome", "2HaveNetFlag: " + HaveNetFlag);
Log.i("welcome", "2Wrongnet: " + WrongNet);
Log.i("welcome", "2html: " + htmlbody);
}
}, 2000);

new Handler().postDelayed(new Runnable() {

@Override
public void run() {
Intent intent = new Intent(WelcomeActivity.this, MainActivity.class);
intent.putExtra("WrongNet", WrongNet);
intent.putExtra("HtmlBody", htmlbody);
startActivity(intent);
WelcomeActivity.this.finish();

}

}, 2500);
}