mirror of https://github.com/FongMi/TV.git
parent
07bc3d9842
commit
6c3631cca7
@ -0,0 +1,94 @@ |
||||
package com.fongmi.android.tv.ui.custom; |
||||
|
||||
import android.app.Activity; |
||||
import android.content.DialogInterface; |
||||
import android.view.LayoutInflater; |
||||
import android.view.View; |
||||
import android.view.WindowManager; |
||||
import android.view.inputmethod.EditorInfo; |
||||
|
||||
import androidx.appcompat.app.AlertDialog; |
||||
|
||||
import com.fongmi.android.tv.databinding.DialogConfigBinding; |
||||
import com.fongmi.android.tv.event.ServerEvent; |
||||
import com.fongmi.android.tv.server.Server; |
||||
import com.fongmi.android.tv.utils.Prefers; |
||||
import com.fongmi.android.tv.utils.QRCode; |
||||
import com.fongmi.android.tv.utils.ResUtil; |
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder; |
||||
|
||||
import org.greenrobot.eventbus.EventBus; |
||||
import org.greenrobot.eventbus.Subscribe; |
||||
import org.greenrobot.eventbus.ThreadMode; |
||||
|
||||
public class ConfigDialog implements DialogInterface.OnDismissListener { |
||||
|
||||
private DialogConfigBinding binding; |
||||
private AlertDialog dialog; |
||||
private Callback callback; |
||||
|
||||
public static void show(Activity activity) { |
||||
new ConfigDialog().create(activity); |
||||
} |
||||
|
||||
public void create(Activity activity) { |
||||
callback = (Callback) activity; |
||||
binding = DialogConfigBinding.inflate(LayoutInflater.from(activity)); |
||||
dialog = new MaterialAlertDialogBuilder(activity).setView(binding.getRoot()).create(); |
||||
EventBus.getDefault().register(this); |
||||
initDialog(); |
||||
initView(); |
||||
initEvent(); |
||||
} |
||||
|
||||
private void initDialog() { |
||||
WindowManager.LayoutParams params = dialog.getWindow().getAttributes(); |
||||
params.width = (int) (ResUtil.getScreenWidthPx() * 0.65f); |
||||
dialog.getWindow().setAttributes(params); |
||||
dialog.getWindow().setDimAmount(0); |
||||
dialog.setOnDismissListener(this); |
||||
dialog.show(); |
||||
} |
||||
|
||||
private void initView() { |
||||
binding.text.setText(Prefers.getUrl()); |
||||
binding.text.setSelection(binding.text.getText().length()); |
||||
binding.code.setImageBitmap(QRCode.getBitmap(Server.get().getAddress(false), 200)); |
||||
} |
||||
|
||||
private void initEvent() { |
||||
binding.positive.setOnClickListener(this::onPositive); |
||||
binding.negative.setOnClickListener(this::onNegative); |
||||
binding.text.setOnEditorActionListener((textView, actionId, event) -> { |
||||
if (actionId == EditorInfo.IME_ACTION_DONE) binding.positive.performClick(); |
||||
return true; |
||||
}); |
||||
} |
||||
|
||||
private void onPositive(View view) { |
||||
Prefers.putUrl(binding.text.getText().toString().trim()); |
||||
callback.setConfig(); |
||||
dialog.dismiss(); |
||||
} |
||||
|
||||
private void onNegative(View view) { |
||||
dialog.dismiss(); |
||||
} |
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN) |
||||
public void onServerEvent(ServerEvent event) { |
||||
if (event.getType() != ServerEvent.Type.API) return; |
||||
binding.text.setText(event.getText()); |
||||
binding.text.setSelection(binding.text.getText().length()); |
||||
} |
||||
|
||||
@Override |
||||
public void onDismiss(DialogInterface dialogInterface) { |
||||
EventBus.getDefault().unregister(this); |
||||
} |
||||
|
||||
public interface Callback { |
||||
|
||||
void setConfig(); |
||||
} |
||||
} |
||||
@ -1,20 +1,67 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
android:orientation="vertical" |
||||
android:paddingStart="16dp" |
||||
android:paddingTop="16dp" |
||||
android:paddingEnd="16dp"> |
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:layout_width="wrap_content" |
||||
android:layout_height="wrap_content" |
||||
android:padding="24dp"> |
||||
|
||||
<ImageView |
||||
android:id="@+id/code" |
||||
android:layout_width="150dp" |
||||
android:layout_height="150dp" |
||||
android:scaleType="fitXY" /> |
||||
|
||||
<EditText |
||||
android:id="@+id/text" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginStart="24dp" |
||||
android:layout_toEndOf="@+id/code" |
||||
android:hint="@string/dialog_config_url_hint" |
||||
android:imeOptions="actionDone" |
||||
android:importantForAutofill="no" |
||||
android:inputType="text" |
||||
android:singleLine="true" |
||||
android:textSize="18sp" /> |
||||
|
||||
</LinearLayout> |
||||
<LinearLayout |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
android:layout_alignStart="@+id/text" |
||||
android:layout_alignBottom="@+id/code" |
||||
android:layout_marginTop="16dp" |
||||
android:orientation="horizontal"> |
||||
|
||||
<Button |
||||
android:id="@+id/history" |
||||
android:layout_width="0dp" |
||||
android:layout_height="wrap_content" |
||||
android:layout_weight="1" |
||||
android:backgroundTint="@color/grey_700" |
||||
android:singleLine="true" |
||||
android:text="@string/setting_history" |
||||
android:textColor="@color/white" /> |
||||
|
||||
<Button |
||||
android:id="@+id/negative" |
||||
android:layout_width="0dp" |
||||
android:layout_height="wrap_content" |
||||
android:layout_marginStart="16dp" |
||||
android:layout_marginEnd="16dp" |
||||
android:layout_weight="1" |
||||
android:backgroundTint="@color/grey_700" |
||||
android:singleLine="true" |
||||
android:text="@string/dialog_negative" |
||||
android:textColor="@color/white" /> |
||||
|
||||
<Button |
||||
android:id="@+id/positive" |
||||
android:layout_width="0dp" |
||||
android:layout_height="wrap_content" |
||||
android:layout_weight="1" |
||||
android:backgroundTint="@color/grey_700" |
||||
android:singleLine="true" |
||||
android:text="@string/dialog_positive" |
||||
android:textColor="@color/white" /> |
||||
|
||||
</LinearLayout> |
||||
</RelativeLayout> |
||||
@ -0,0 +1,36 @@ |
||||
package com.fongmi.android.tv.event; |
||||
|
||||
public class ServerEvent { |
||||
|
||||
private final String text; |
||||
private final Type type; |
||||
|
||||
public static ServerEvent search(String text) { |
||||
return new ServerEvent(Type.SEARCH, text); |
||||
} |
||||
|
||||
public static ServerEvent push(String text) { |
||||
return new ServerEvent(Type.PUSH, text); |
||||
} |
||||
|
||||
public static ServerEvent api(String text) { |
||||
return new ServerEvent(Type.API, text); |
||||
} |
||||
|
||||
public ServerEvent(Type type, String text) { |
||||
this.type = type; |
||||
this.text = text; |
||||
} |
||||
|
||||
public Type getType() { |
||||
return type; |
||||
} |
||||
|
||||
public String getText() { |
||||
return text; |
||||
} |
||||
|
||||
public enum Type { |
||||
SEARCH, PUSH, API |
||||
} |
||||
} |
||||
@ -1,58 +1,103 @@ |
||||
package com.fongmi.android.tv.server; |
||||
|
||||
import com.fongmi.android.tv.R; |
||||
import com.fongmi.android.tv.api.ApiConfig; |
||||
import com.fongmi.android.tv.server.process.InputRequestProcess; |
||||
import com.fongmi.android.tv.server.process.RawRequestProcess; |
||||
import com.fongmi.android.tv.server.process.RequestProcess; |
||||
|
||||
import java.io.InputStream; |
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.regex.Matcher; |
||||
import java.util.regex.Pattern; |
||||
|
||||
import fi.iki.elonen.NanoHTTPD; |
||||
|
||||
public class Nano extends NanoHTTPD { |
||||
|
||||
private Listener mListener; |
||||
private List<RequestProcess> processes; |
||||
private Listener listener; |
||||
|
||||
public Nano() { |
||||
super(9978); |
||||
addRequestProcess(); |
||||
} |
||||
|
||||
private void addRequestProcess() { |
||||
processes = new ArrayList<>(); |
||||
processes.add(new InputRequestProcess(this)); |
||||
processes.add(new RawRequestProcess("/", R.raw.index, NanoHTTPD.MIME_HTML)); |
||||
processes.add(new RawRequestProcess("/index.html", R.raw.index, NanoHTTPD.MIME_HTML)); |
||||
processes.add(new RawRequestProcess("/ui.css", R.raw.ui, "text/css")); |
||||
processes.add(new RawRequestProcess("/style.css", R.raw.style, "text/css")); |
||||
processes.add(new RawRequestProcess("/jquery.js", R.raw.jquery, "application/x-javascript")); |
||||
processes.add(new RawRequestProcess("/script.js", R.raw.script, "application/x-javascript")); |
||||
processes.add(new RawRequestProcess("/favicon.ico", R.mipmap.ic_launcher, "image/x-icon")); |
||||
} |
||||
|
||||
public Listener getListener() { |
||||
return listener; |
||||
} |
||||
|
||||
public void setListener(Listener listener) { |
||||
this.listener = listener; |
||||
} |
||||
|
||||
@Override |
||||
public Response serve(IHTTPSession session) { |
||||
if (session.getUri().isEmpty()) return super.serve(session); |
||||
String url = session.getUri().trim(); |
||||
if (url.indexOf('?') >= 0) url = url.substring(0, url.indexOf('?')); |
||||
if (session.getMethod() == Method.GET) { |
||||
if (url.equals("/proxy")) { |
||||
Map<String, String> params = session.getParms(); |
||||
if (params.containsKey("do")) { |
||||
Object[] rs = ApiConfig.get().proxyLocal(params); |
||||
try { |
||||
int code = (int) rs[0]; |
||||
String mime = (String) rs[1]; |
||||
InputStream stream = rs[2] != null ? (InputStream) rs[2] : null; |
||||
return NanoHTTPD.newChunkedResponse(Response.Status.lookup(code), mime, stream); |
||||
} catch (Exception e) { |
||||
return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "500"); |
||||
} |
||||
if (url.contains("?")) url = url.substring(0, url.indexOf('?')); |
||||
if (session.getMethod() == Method.POST) parseBody(session); |
||||
for (RequestProcess process : processes) { |
||||
if (process.isRequest(session, url)) { |
||||
return process.doResponse(session, url); |
||||
} |
||||
} |
||||
if (session.getMethod() == Method.GET && url.equals("/proxy")) { |
||||
Map<String, String> params = session.getParms(); |
||||
if (params.containsKey("do")) { |
||||
Object[] rs = ApiConfig.get().proxyLocal(params); |
||||
try { |
||||
int code = (int) rs[0]; |
||||
String mime = (String) rs[1]; |
||||
InputStream stream = rs[2] != null ? (InputStream) rs[2] : null; |
||||
return NanoHTTPD.newChunkedResponse(Response.Status.lookup(code), mime, stream); |
||||
} catch (Exception e) { |
||||
return NanoHTTPD.newFixedLengthResponse(NanoHTTPD.Response.Status.INTERNAL_ERROR, NanoHTTPD.MIME_PLAINTEXT, "500"); |
||||
} |
||||
} |
||||
} |
||||
return super.serve(session); |
||||
return processes.get(0).doResponse(session, ""); |
||||
} |
||||
|
||||
public Listener getListener() { |
||||
return mListener; |
||||
private void parseBody(IHTTPSession session) { |
||||
Map<String, String> files = new HashMap<>(); |
||||
try { |
||||
String hd = session.getHeaders().get("content-type"); |
||||
if (hd == null) return; |
||||
if (hd.toLowerCase().contains("multipart/form-data") && !hd.toLowerCase().contains("charset=")) { |
||||
Matcher matcher = Pattern.compile("[ |\t]*(boundary[ |\t]*=[ |\t]*['|\"]?[^\"^'^;^,]*['|\"]?)", Pattern.CASE_INSENSITIVE).matcher(hd); |
||||
String boundary = matcher.find() ? matcher.group(1) : null; |
||||
if (boundary != null) session.getHeaders().put("content-type", "multipart/form-data; charset=utf-8; " + boundary); |
||||
} |
||||
session.parseBody(files); |
||||
} catch (Exception ignored) { |
||||
} |
||||
} |
||||
|
||||
public void setListener(Listener listener) { |
||||
this.mListener = listener; |
||||
public static Response createPlainTextResponse(Response.IStatus status, String text) { |
||||
return newFixedLengthResponse(status, NanoHTTPD.MIME_PLAINTEXT, text); |
||||
} |
||||
|
||||
public interface Listener { |
||||
|
||||
void onTextReceived(String text); |
||||
void onSearch(String text); |
||||
|
||||
void onApiReceived(String url); |
||||
void onPush(String url); |
||||
|
||||
void onPushReceived(String url); |
||||
void onApi(String url); |
||||
} |
||||
} |
||||
|
||||
@ -0,0 +1,39 @@ |
||||
package com.fongmi.android.tv.server.process; |
||||
|
||||
import com.fongmi.android.tv.server.Nano; |
||||
|
||||
import java.util.Map; |
||||
|
||||
import fi.iki.elonen.NanoHTTPD; |
||||
|
||||
public class InputRequestProcess implements RequestProcess { |
||||
|
||||
private final Nano nano; |
||||
|
||||
public InputRequestProcess(Nano nano) { |
||||
this.nano = nano; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isRequest(NanoHTTPD.IHTTPSession session, String path) { |
||||
return session.getMethod() == NanoHTTPD.Method.POST && path.equals("/action"); |
||||
} |
||||
|
||||
@Override |
||||
public NanoHTTPD.Response doResponse(NanoHTTPD.IHTTPSession session, String path) { |
||||
if (!path.equals("/action")) return Nano.createPlainTextResponse(NanoHTTPD.Response.Status.NOT_FOUND, "Error 404, file not found."); |
||||
Map<String, String> params = session.getParms(); |
||||
switch (params.get("do")) { |
||||
case "search": |
||||
nano.getListener().onSearch(params.get("word").trim()); |
||||
break; |
||||
case "push": |
||||
nano.getListener().onPush(params.get("url").trim()); |
||||
break; |
||||
case "api": |
||||
nano.getListener().onApi(params.get("url").trim()); |
||||
break; |
||||
} |
||||
return Nano.createPlainTextResponse(NanoHTTPD.Response.Status.OK, "ok"); |
||||
} |
||||
} |
||||
@ -0,0 +1,37 @@ |
||||
package com.fongmi.android.tv.server.process; |
||||
|
||||
import com.fongmi.android.tv.App; |
||||
import com.fongmi.android.tv.server.Nano; |
||||
|
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
|
||||
import fi.iki.elonen.NanoHTTPD; |
||||
|
||||
public class RawRequestProcess implements RequestProcess { |
||||
|
||||
private final String mimeType; |
||||
private final String path; |
||||
private final int resId; |
||||
|
||||
public RawRequestProcess(String path, int resId, String mimeType) { |
||||
this.path = path; |
||||
this.resId = resId; |
||||
this.mimeType = mimeType; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isRequest(NanoHTTPD.IHTTPSession session, String path) { |
||||
return session.getMethod() == NanoHTTPD.Method.GET && path.equalsIgnoreCase(this.path); |
||||
} |
||||
|
||||
@Override |
||||
public NanoHTTPD.Response doResponse(NanoHTTPD.IHTTPSession session, String path) { |
||||
try { |
||||
InputStream is = App.get().getResources().openRawResource(resId); |
||||
return Nano.newFixedLengthResponse(NanoHTTPD.Response.Status.OK, mimeType + ";charset=utf-8", is, is.available()); |
||||
} catch (IOException IOExc) { |
||||
return Nano.createPlainTextResponse(NanoHTTPD.Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + IOExc.getMessage()); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,10 @@ |
||||
package com.fongmi.android.tv.server.process; |
||||
|
||||
import fi.iki.elonen.NanoHTTPD; |
||||
|
||||
public interface RequestProcess { |
||||
|
||||
boolean isRequest(NanoHTTPD.IHTTPSession session, String path); |
||||
|
||||
NanoHTTPD.Response doResponse(NanoHTTPD.IHTTPSession session, String path); |
||||
} |
||||
@ -0,0 +1,43 @@ |
||||
package com.fongmi.android.tv.utils; |
||||
|
||||
import android.graphics.Bitmap; |
||||
|
||||
import com.google.zxing.BarcodeFormat; |
||||
import com.google.zxing.EncodeHintType; |
||||
import com.google.zxing.MultiFormatWriter; |
||||
import com.google.zxing.common.BitMatrix; |
||||
|
||||
import java.util.EnumMap; |
||||
import java.util.Map; |
||||
|
||||
public class QRCode { |
||||
|
||||
private static final int WHITE = 0xFFFFFFFF; |
||||
private static final int BLACK = 0xFF000000; |
||||
|
||||
public static Bitmap createBitmap(BitMatrix matrix) { |
||||
int width = matrix.getWidth(); |
||||
int height = matrix.getHeight(); |
||||
int[] pixels = new int[width * height]; |
||||
for (int y = 0; y < height; y++) { |
||||
int offset = y * width; |
||||
for (int x = 0; x < width; x++) { |
||||
pixels[offset + x] = matrix.get(x, y) ? BLACK : WHITE; |
||||
} |
||||
} |
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); |
||||
bitmap.setPixels(pixels, 0, width, 0, 0, width, height); |
||||
return bitmap; |
||||
} |
||||
|
||||
public static Bitmap getBitmap(String contents, int size) { |
||||
try { |
||||
Map<EncodeHintType, Object> hints = new EnumMap<>(EncodeHintType.class); |
||||
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8"); |
||||
hints.put(EncodeHintType.MARGIN, 0); |
||||
return createBitmap(new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE, ResUtil.dp2px(size), ResUtil.dp2px(size), hints)); |
||||
} catch (Exception e) { |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,110 @@ |
||||
<!DOCTYPE html> |
||||
<html lang="zh-cmn-Hans"> |
||||
|
||||
<head> |
||||
<meta charset="UTF-8"> |
||||
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0,viewport-fit=cover"> |
||||
<meta name="wechat-enable-text-zoom-em" content="true"> |
||||
<title>TV</title> |
||||
<meta name="layoutmode" content="standard"> |
||||
<link rel="stylesheet" type="text/css" href="style.css"> |
||||
<link rel="stylesheet" type="text/css" href="ui.css"> |
||||
</head> |
||||
|
||||
<body> |
||||
<div class="page"> |
||||
<div class="page__bd" style="height: 100%;"> |
||||
<div class="weui-tab"> |
||||
<div id="panel1" role="tabpanel" aria-labelledby="tab1" class="weui-tab__panel"> |
||||
<div class="weui-form"> |
||||
<div class="weui-form__text-area"> |
||||
<h2 class="weui-form__title">搜尋</h2> |
||||
</div> |
||||
<div class="weui-form__control-area"> |
||||
<div class="weui-cells__group weui-cells__group_form"> |
||||
<div class="weui-cells"> |
||||
<div class="weui-cell weui-cell_active weui-cell_vcode weui-cell_wrap"> |
||||
<div class="weui-cell__bd weui-flex"> |
||||
<input id="search_key_word" class="weui-input weui-cell__control weui-cell__control_flex" type="text" value="" placeholder="請輸入搜尋的關鍵字..." /> |
||||
<button onclick="search(); return false;" class="weui-cell__control weui-btn weui-btn_default weui-vcode-btn">搜尋</button> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div id="panel2" role="tabpanel" aria-labelledby="tab2" class="weui-tab__panel" style="display: none;"> |
||||
<div class="weui-form"> |
||||
<div class="weui-form__text-area"> |
||||
<h2 class="weui-form__title">推送</h2> |
||||
</div> |
||||
<div class="weui-form__control-area"> |
||||
<div class="weui-cells__group weui-cells__group_form"> |
||||
<div class="weui-cells"> |
||||
<div class="weui-cell weui-cell_active weui-cell_vcode weui-cell_wrap"> |
||||
<div class="weui-cell__bd weui-flex"> |
||||
<input id="push_url" class="weui-input weui-cell__control weui-cell__control_flex" type="text" value="" placeholder="請輸入需要推送播放的地址..." /> |
||||
<button onclick="push(); return false;" class="weui-cell__control weui-btn weui-btn_default weui-vcode-btn">推送</button> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div id="panel3" role="tabpanel" aria-labelledby="tab3" class="weui-tab__panel" style="display: none;"> |
||||
<div class="weui-form"> |
||||
<div class="weui-form__text-area"> |
||||
<h2 class="weui-form__title">接口</h2> |
||||
</div> |
||||
<div class="weui-form__control-area"> |
||||
<div class="weui-cells__group weui-cells__group_form"> |
||||
<div class="weui-cells"> |
||||
<div class="weui-cell weui-cell_active weui-cell_vcode weui-cell_wrap"> |
||||
<div class="weui-cell__bd weui-flex"> |
||||
<input id="diy_api_url" class="weui-input weui-cell__control weui-cell__control_flex" type="text" value="" placeholder="請輸入自定義配置接口地址..." /> |
||||
<button onclick="api(); return false;" class="weui-cell__control weui-btn weui-btn_default weui-vcode-btn">確定</button> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div role="tablist" class="weui-tabbar"> |
||||
<div id="tab1" role="tab" aria-labelledby="t1_title" aria-selected="true" aria-controls="panel1" class="weui-tabbar__item weui-bar__item_on"> |
||||
<img style="width: 20px; height: 20px; margin: 5px 0 5px 0;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAuQAAALkB4qdB6AAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAKfSURBVFiFxdfPi1ZVGMDxz31HCnOq0ZkK00THlxhpIToELdxILkQMRMN1bly1EAlxY/0BswqiFi7KnW3aBSJFKTg2iJY/CsIfEZqjmSJiipKdFvfR9/Jy7zv3bd55PXA4l3vO8zzf85zznPOcLKWkU8mybCE2YhPGsATDuIGr+Blf43BK6U5HZWUlpVRaMQ/78ACpRr2H3WhU6SyrWZkHsixbjc+xJpSfwUmcivZCeGMtxqO+EeLHsCOldL5rD2AAH+FhGP4Nb9eZCd7BHwVv7Kol16bkk1DwLz7FYDfuxBC+KCzLh7UBsLMguLUbwyUgOwoT2T4jABbjbgh9NhvjBYiDoe8vDM0EsD8GX8CCHgEskodpwkSHcYbxT7hrXS+MF5RvCoC/8VwVwHsx6JdeGi8YmA79W8r6G9gcEXmiVtx2X05Gu7mss4FmnwCaZZ0N+dneD4AlZZ0NDMb39TkCeKz3mSqA6fheO0cAa6I9XQVwJb7fnCOA8WhPVQFMPk0AeEsep7cxv8dnwItaN+trVQdRhvMx6OMeAxwIvd/OdBe8q3V7re+R8S1auUGzI0AIHAmB3/HCLI2/JA+/hL0dxxaEFssjIuEoVvxP42PyQy3JQ29eLYAQHsefIXwX75PnjTUMD2AP7mslNsfxfG2Agvu+LCj5PtazaheviD00VZCZLExkstOSdprRtsI6Pq43cEieO36Dm239l0QKJs+Sr8X/KRVZ0UxuHcEEfm0zVKz38SM+wLNt8qu08oETWNhuo/RdUFayLFup9TIakqdv53AxpfSog9zr+A6vBuiGlNKtJwNmE25dREYTl8MTP2Gk1hL0GGJUfsYknMXLfQUIiOXy11aSP2pfqb0HelWyLFsm3xOj+KHvAAGxFF/h8n+GU+2I7XnhZwAAAABJRU5ErkJggg==" alt="" class="weui-tabbar__icon"> |
||||
<p id="t1_title" aria-hidden="true" class="weui-tabbar__label">搜尋</p> |
||||
</div> |
||||
<div id="tab2" role="tab" aria-labelledby="t2_title" aria-selected="false" aria-controls="panel2" class="weui-tabbar__item"> |
||||
<img style="width: 20px; height: 20px; margin: 5px 0 5px 0;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAuQAAALkB4qdB6AAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAJlSURBVFiF7ddNSNRBGMfxzy6B4aUsoxfMXiQwO1kEHewQXUI6FXWqIIzQgii69XILOnToFlYE3SMSkiCJolsW7CUIen+j6FJBiGWm02FHG9fddZdELw48/Hdmfs/zfGfmYZjNhBDMZsvOavY5AMyrRpzJZLagHRuj1eAFnqI7hJCrmiCEMKVhIa5gFKGM9aGhkpjjsStI3orPMcEPXMQeNKAeW3Ea36LmEzZNCwAW4FUM/BItZbR1uBm1X7F8OgBuxIDPUFfBbtXgYfS5/V8A2JycbXvFW8pajCS71o29qK8W4FQMcr/EfAOeIIfVBXM9RQp0AIeqAbgfHSc7sTKpjYB3WJPMz0MzduMs3ibam1hUEgDz0Ymh6NBWJPnrIit8n0IU+NTiPH4X7mqhsANfCgLXJ/ONSfJconmUQKwts6ttCUTHBACcSwL24WB6tliFN0nyRYl+QQLxAU1lIE5E3XcsGxs8GQcH0VnEaVVyjrmxMxwDiL9TiI9TQIzV11FYKn/DBewv4fCkMHkhQBGIp2UAuqLmGlyInZ4yDpfRmyYvBpBA3ML1MvGakgV5EDu7SjmUCTQJoEK/xdH3RxYb5Fu/mWvN8fssi+HYGZxBgG3x25+VLzBYNxOZM5lMLY7F7t0sHsfOvpkAwHEswb0Qwh1YIf+YGMSOKoupF71V6Lfjj/yxt4zfhHH1IU4crraqK0zeKv9QCTgyPp4ILvl3teZwBuunKXkHfsbYJybMFQh3+vf+m24bQNckuCK0C3EAV/Ecv6INRfsdbTixP9FGoo2a+ILuQWOx3cnEpLPWZv2f0RzAX4u0r6Ikm0C7AAAAAElFTkSuQmCC" alt="" class="weui-tabbar__icon"> |
||||
<p aria-hidden="true" id="t2_title" class="weui-tabbar__label">推送</p> |
||||
</div> |
||||
<div id="tab3" role="tab" aria-labelledby="t3_title" aria-selected="false" aria-controls="panel3" class="weui-tabbar__item"> |
||||
<img style="width: 20px; height: 20px; margin: 5px 0 5px 0;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAAC5AAAAuQHip0HoAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAANtQTFRF////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhQHXOQAAAEh0Uk5TAAECBAUGCQoNDxITGiAhKC4xMjM0Nzg8P0BDT1dmbHR4fX+MlpedoKOkp6issri5vL2/zs/S19zd4OHi5OXp7e/x8/f6+/z+bkNkuAAAATRJREFUOMuNk2kzAmAUhR8iUSHSokWobCFLtFBI9fz/X+RDzdQY9brfzl3mbufAghVfVF+KLLHoh6p+RJckHGkxmSzq0ZKEjF2ArplfgcPjHYBC2w5Ax3YBYOf4cBovjfw82dy71/ElwOVY7/c2Tz4dlQB2h6q9b3uVxLQiUen53VMd7gINWwflgT7G503jjzoop55sQN5JBuK12sbiWBu1WhzSE/PcectSu/WOjs31uWcrm92ao/WmHc70OTtzbNfHOq5vz2D2Wc9Yq3zpBQCxV+339TUGwIV+VdaAxIMtAKq+5SD3bhWAlg+zvc+9BogMzQHkHEYAbjyf9bryFGDf/hT33Qc49eq/CaEWgSGDawYPFTx18Fl5J+mV76bhU2oVYcKUC5I2TPuwcP4tvaB4/5T/D6drVhrBHvH6AAAAAElFTkSuQmCC" alt="" class="weui-tabbar__icon"> |
||||
<p id="t3_title" aria-hidden="true" class="weui-tabbar__label">接口</p> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<div role="alert" id="warnToast" style="display: none;"> |
||||
<div class="weui-mask_transparent"></div> |
||||
<div class="weui-toast"> |
||||
<i class="weui-icon-warn weui-icon_toast"></i> |
||||
<p class="weui-toast__content" id="warnToastContent"></p> |
||||
</div> |
||||
</div> |
||||
<div role="alert" id="loadingToast" style="display: none;"> |
||||
<div class="weui-mask_transparent"></div> |
||||
<div class="weui-toast"> |
||||
<span class="weui-primary-loading weui-icon_toast"><span class="weui-primary-loading__dot"></span></span> |
||||
<p class="weui-toast__content">載入中</p> |
||||
</div> |
||||
</div> |
||||
<script src="jquery.js"></script> |
||||
<script src="script.js"></script> |
||||
</body> |
||||
|
||||
</html> |
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,42 @@ |
||||
function search() { |
||||
doAction('search', {word: $('#search_key_word').val()}); |
||||
} |
||||
|
||||
function api() { |
||||
doAction('api', {url: $('#diy_api_url').val()}); |
||||
} |
||||
|
||||
function push() { |
||||
doAction('push', {url: $('#push_url').val()}); |
||||
} |
||||
|
||||
function doAction(action, kv) { |
||||
kv['do'] = action; |
||||
$.post('/action', kv, function (data) { |
||||
console.log(data); |
||||
}); |
||||
return false; |
||||
} |
||||
|
||||
function warnToast(msg) { |
||||
$('#warnToastContent').html(msg); |
||||
$('#warnToast').show(); |
||||
setTimeout(() => { |
||||
$('#warnToast').hide(); |
||||
}, 1000); |
||||
} |
||||
|
||||
function showPanel(id) { |
||||
let tab = $('#tab' + id)[0]; |
||||
$(tab).attr('aria-selected', 'true').addClass('weui-bar__item_on'); |
||||
$(tab).siblings('.weui-bar__item_on').removeClass('weui-bar__item_on').attr('aria-selected', 'false'); |
||||
var panelId = '#' + $(tab).attr('aria-controls'); |
||||
$(panelId).css('display', 'block'); |
||||
$(panelId).siblings('.weui-tab__panel').css('display', 'none'); |
||||
} |
||||
|
||||
$(function () { |
||||
$('.weui-tabbar__item').on('click', function () { |
||||
showPanel(parseInt($(this).attr('id').substr(3))); |
||||
}); |
||||
}); |
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,26 @@ |
||||
body, |
||||
html { |
||||
height: 100%; |
||||
-webkit-tap-highlight-color: transparent; |
||||
} |
||||
|
||||
body { |
||||
font-family: system-ui, -apple-system, Helvetica Neue, sans-serif; |
||||
} |
||||
|
||||
.page, |
||||
body { |
||||
background-color: var(--weui-BG-0); |
||||
} |
||||
|
||||
.page { |
||||
position: absolute; |
||||
top: 0; |
||||
right: 0; |
||||
bottom: 0; |
||||
left: 0; |
||||
overflow-y: auto; |
||||
-webkit-overflow-scrolling: touch; |
||||
box-sizing: border-box; |
||||
z-index: 1; |
||||
} |
||||
Loading…
Reference in new issue