Android开发的一些轮子整理(持续更新)
标签搜索

Android开发的一些轮子整理(持续更新)

Pop.Kite
2022-03-03 / 0 评论 / 194 阅读 / 正在检测是否收录...

独立进程记录日志

    class Activity{
        private lateinit var process: Process
        override fun onCreate(){
            val logFile = File("/sdcard/Documents/", "${System.currentTimeMillis()}.log")
            Runtime.getRuntime().exec("logcat -c")
            process = Runtime.getRuntime().exec("logcat -f $logFile") 
        }
    
        override fun onDestory(){
            if(process.isAlive) {
               process.destory()
            }
        }
    }

动态请求存储权限

    fun requestPermission(context: Context) {
        if (ContextCompat.checkSelfPermission(context, 
                Manifest.permission.WRITE_EXTERNAL_STORAGE) 
                != PackageManager.PERMISSION_GRANTED
            || ContextCompat.checkSelfPermission(context, 
            Manifest.permission.READ_EXTERNAL_STORAGE) 
            != PackageManager.PERMISSION_GRANTED)
        {
            Toast.makeText(context, "申请权限", Toast.LENGTH_SHORT).show()
    
            // 申请 相机 麦克风权限
            ActivityCompat.requestPermissions(
                context as Activity,
                arrayOf(
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                ),
                100
            )
        }
    }

是否具有读写权限

    fun isExternalStorageWritable(): Boolean {
        val state = Environment.getExternalStorageState()
        if (Environment.MEDIA_MOUNTED == state) {
            return true
        }
        return false
    }
    
    /* Checks if external storage is available to at least read */
    fun isExternalStorageReadable(): Boolean {
        val state = Environment.getExternalStorageState()
        if (Environment.MEDIA_MOUNTED == state ||
            Environment.MEDIA_MOUNTED_READ_ONLY == state
        ) {
            return true
        }
        return false
    }

隐藏手机软键盘

    fun hideKeyboard(context: Context) {
        val imm: InputMethodManager =
            context.getSystemService(Context.INPUT_METHOD_SERVICE)
            as InputMethodManager
        if (imm.isActive) {
            if ((context as Activity).currentFocus?.windowToken != null) {
                imm.hideSoftInputFromWindow(
                    context.currentFocus!!.windowToken,
                    InputMethodManager.HIDE_NOT_ALWAYS
                )
            }
        }
    }

偏好设置工具类

    class SharePreferenceUtil(mContext: Context, name: String) {
        private val mSharePreferences: SharedPreferences
        private val mEditor: SharedPreferences.Editor
        
        private fun putInt(key: String, value: Int) {
            mEditor.putInt(key, value).apply()
        }
    
        private fun getInt(key: String, defaultValue: Int): Int {
            return mSharePreferences.getInt(key, defaultValue)
        }
    
        private fun putLong(key: String, value: Long) {
            mEditor.putLong(key, value).apply()
        }
    
        private fun getLong(key: String, defaultValue: Long): Long {
            return mSharePreferences.getLong(key, defaultValue)
        }
    
        private fun putFloat(key: String, value: Float) {
            mEditor.putFloat(key, value).apply()
        }
    
        private fun getFloat(key: String, defaultValue: Float): Float {
            return mSharePreferences.getFloat(key, defaultValue)
        }
    
        private fun putString(key: String, value: String) {
            mEditor.putString(key, value).apply()
        }
    
        private fun getString(key: String, defaultValue: String): String {
            var string = mSharePreferences.getString(key, defaultValue)
            if (string == null) {
                string = ""
            }
            return string
        }
    
        private fun putBoolean(key: String, value: Boolean) {
            mEditor.putBoolean(key, value).apply()
        }
        private fun getBoolean(key: String, defaultValue: Boolean): Boolean {
            return mSharePreferences.getBoolean(key, defaultValue)
        }
    
        fun put(key: String, value: Any) {
            when (value) {
                is Int -> putInt(key, value)
                is Long -> putLong(key, value)
                is Float -> putFloat(key, value)
                is String -> putString(key, value)
                is Boolean -> putBoolean(key, value)
                else -> return
            }
        }
    
        fun <T> get(key: String, defaultObject: T): T {
            when (defaultObject) {
                is String -> {
                    return getString(key, defaultObject as String) as T
                }
                is Int -> {
                    return Integer.valueOf(getInt(key, defaultObject as Int)) as T
                }
                is Boolean -> {
                    return java.lang.Boolean.valueOf(getBoolean(key, defaultObject as Boolean)) as T
                }
                is Long -> {
                    return java.lang.Long.valueOf(getLong(key, defaultObject as Long)) as T
                }
                is Float -> {
                    return java.lang.Float.valueOf(getFloat(key, defaultObject as Float)) as T
                }
                else -> return defaultObject
            }
        }
    
        init {
            mSharePreferences = mContext.getSharedPreferences(name, Context.MODE_PRIVATE)
            mEditor = mSharePreferences.edit()
        }
    }

##PCM转WAV工具类

    import android.media.AudioFormat;
    import android.media.AudioRecord;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class PcmToWavUtil {
        private final int mBufferSize; //缓存的音频大小
        private int mSampleRate = 8000;// 8000|16000
        private int mChannel = AudioFormat.CHANNEL_IN_STEREO; //立体声
        private int mEncoding = AudioFormat.ENCODING_PCM_16BIT;
    
        public PcmToWavUtil() {
            this.mBufferSize = AudioRecord.getMinBufferSize(mSampleRate, mChannel, mEncoding);
        }
    
        public PcmToWavUtil(int sampleRate, int channel, int encoding) {
            this.mSampleRate = sampleRate;
            this.mChannel = channel;
            this.mEncoding = encoding;
            this.mBufferSize = AudioRecord.getMinBufferSize(mSampleRate, mChannel, mEncoding);
        }
    
        public void pcmToWav(String inFilename, String outFilename) {
            FileInputStream in;
            FileOutputStream out;
            long totalAudioLen;
            long totalDataLen;
            long longSampleRate = mSampleRate;
            int channels = 2;
            long byteRate = 16L * mSampleRate * channels / 8;
            byte[] data = new byte[mBufferSize];
            try {
                in = new FileInputStream(inFilename);
                out = new FileOutputStream(outFilename);
                totalAudioLen = in.getChannel().size();
                totalDataLen = totalAudioLen + 36;
    
                writeWaveFileHeader(out, totalAudioLen, totalDataLen,
                        longSampleRate, channels, byteRate);
                while (in.read(data) != -1) {
                    out.write(data);
                }
                in.close();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private void writeWaveFileHeader(FileOutputStream out, long totalAudioLen,
                                         long totalDataLen, long longSampleRate, int channels, long byteRate)
                throws IOException {
            byte[] header = new byte[44];
            header[0] = 'R'; // RIFF/WAVE header
            header[1] = 'I';
            header[2] = 'F';
            header[3] = 'F';
            header[4] = (byte) (totalDataLen & 0xff);
            header[5] = (byte) ((totalDataLen >> 8) & 0xff);
            header[6] = (byte) ((totalDataLen >> 16) & 0xff);
            header[7] = (byte) ((totalDataLen >> 24) & 0xff);
            header[8] = 'W'; //WAVE
            header[9] = 'A';
            header[10] = 'V';
            header[11] = 'E';
            header[12] = 'f'; // 'fmt ' chunk
            header[13] = 'm';
            header[14] = 't';
            header[15] = ' ';
            header[16] = 16; // 4 bytes: size of 'fmt ' chunk
    
            header[17] = 0;
            header[18] = 0;
            header[19] = 0;
            header[20] = 1; // format = 1
            header[21] = 0;
            header[22] = (byte) channels;
            header[23] = 0;
            header[24] = (byte) (longSampleRate & 0xff);
            header[25] = (byte) ((longSampleRate >> 8) & 0xff);
            header[26] = (byte) ((longSampleRate >> 16) & 0xff);
            header[27] = (byte) ((longSampleRate >> 24) & 0xff);
            header[28] = (byte) (byteRate & 0xff);
            header[29] = (byte) ((byteRate >> 8) & 0xff);
            header[30] = (byte) ((byteRate >> 16) & 0xff);
            header[31] = (byte) ((byteRate >> 24) & 0xff);
            header[32] = (byte) (2 * 16 / 8); // block align
            header[33] = 0;
            header[34] = 16; // bits per sample
            header[35] = 0;
            header[36] = 'd'; //data
            header[37] = 'a';
            header[38] = 't';
            header[39] = 'a';
            header[40] = (byte) (totalAudioLen & 0xff);
            header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
            header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
            header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
            out.write(header, 0, 44);
        }
    }

计算媒体文件时长

    fun computeLength(filePath: String): Int {
        var duration = 0
        try {
            val metaRetriever = MediaMetadataRetriever()
            metaRetriever.setDataSource(filePath)
            duration =
                metaRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
                    .toString().toInt()
            metaRetriever.release()
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return duration
    }

Retrofit简单使用

    //定义接口
    interface NetService {
        @GET("job/?")
        fun downloadFile(@Query("device_id") deviceId: Int): Call<ResponseFileBean>
    
        @GET("file/s3/presign/upload?")
        fun downloadPath(@Query("job_id") jobId: Int): Call<ResponsePathBean>
    
        //useless function temporary
        @Headers("Content-type: application/octet-stream")
        @Multipart
        @PUT(".")
        fun uploadFile(@Part fileBean: MultipartBody.Part): Call<ResponseUploadResultBean>
    
        @Headers("Content-Type: application/json")
        @PUT("job/{job_id}/success")
        fun putResult(@Path("job_id") job_id: Int, @Body jsonBody: RequestBody): Call<ResponseBody>
    }
    
    //定义OKHttpClient
    val OkHttpClientForRequest = OkHttpClient.Builder()
        .readTimeout(60, TimeUnit.SECONDS)
        .connectTimeout(60, TimeUnit.SECONDS)
        .retryOnConnectionFailure(true)
        .writeTimeout(60, TimeUnit.SECONDS)
        .build()
    
    //定义创建器
    object NetServiceCreator {
        private lateinit var mRetrofit: Retrofit
    
        fun <T> create(serviceClass: Class<T>, url: String): T {
            mRetrofit = Retrofit.Builder()
                        .baseUrl(url)
                        .addConverterFactory(GsonConverterFactory.create())
                        .client(OkHttpClientForRequest)
                        .build()
            return mRetrofit.create(serviceClass)
        }
    
        inline fun <reified T> create(url: String): T =
            create(T::class.java, url)
    }
    
    //定义挂起,注意需要在协程作用域中执行
    private suspend fun <T> Call<T>.await(): T {
        return suspendCoroutine { continuation ->
            enqueue(object : Callback<T> {
                override fun onResponse(call: Call<T>, response: Response<T>) {
                    val body = response.body()
                    if (body != null) {
                        continuation.resume(body)
                    }
                }
    
                override fun onFailure(call: Call<T>, t: Throwable) {
                    continuation.resumeWithException(t)
                }
    
            })
        }
    }
    
    //执行请求
    val result = NetServiceCreator.create<NetService>("http://www.xxx.com")
        .downloadFile(11).await()

##OKHttp上传示例

    //定义OKHttpClient
    val OkHttpClientForUpload = OkHttpClient.Builder()
        .readTimeout(60, TimeUnit.SECONDS)
        .connectTimeout(60, TimeUnit.SECONDS)
        .retryOnConnectionFailure(true)
        .writeTimeout(60, TimeUnit.SECONDS)
        .build()
        
    //构建请求
    val tempFile = File(Objects.requireNonNull(xxx))
    val mediaType = "application/octet-stream".toMediaTypeOrNull()
    val requestBody = RequestBody.create(mediaType, tempFile)
    
    val request: Request = Request.Builder()
        .url(uploadUrl)
        .method("PUT", requestBody)
        .addHeader("Content-type", "application/octet-stream")
        .build()
    
    //执行请求
    val response = NormalData.OkHttpClientForUpload.newCall(request).execute()

沉浸透明状态栏

    //沉浸状态栏目
    override fun onCreate(){
        ...
        supportActionBar?.hide()
        window.navigationBarColor = Color.TRANSPARENT
        window.statusBarColor = Color.TRANSPARENT
        ...
    }
    
    //设置透明状态栏
    override fun onCreate(){
        ...
        supportActionBar?.hide()
        window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
        window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
        ...
    }

HSB-> RGB方法

    fun hsbToRgb(hsb: FloatArray): String? {
        val rgb = StringBuffer()
        val hsv = hsb[0]
        val sat = hsb[1]
        val value = hsb[2]
        val hi = hsv / 60 % 6
        val f = hsv / 60 - hi
        val p = value * (1 - sat)
        val q = value * (1 - sat * f)
        val t = value * (1 - sat * (1 - f))
        val V = String.format("%02x", (value * 255).toInt())
        val T = String.format("%02x", (t * 255).toInt())
        val P = String.format("%02x", (p * 255).toInt())
        val Q = String.format("%02x", (q * 255).toInt())
        when (hi.toInt()) {
            0 -> {
                rgb.append(V)
                rgb.append(T)
                rgb.append(P)
            }
            1 -> {
                rgb.append(Q)
                rgb.append(V)
                rgb.append(P)
            }
            2 -> {
                rgb.append(P)
                rgb.append(V)
                rgb.append(T)
            }
            3 -> {
                rgb.append(P)
                rgb.append(Q)
                rgb.append(V)
            }
            4 -> {
                rgb.append(T)
                rgb.append(P)
                rgb.append(V)
            }
            5 -> {
                rgb.append(V)
                rgb.append(P)
                rgb.append(Q)
            }
        }
        return rgb.toString()
    }

安卓垂直进度条

    class VerticalSeekBar2 : AppCompatSeekBar {
        constructor(context: Context?) : super(context!!)
        constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(
            context!!,
            attrs,
            defStyle
        )
    
        constructor(context: Context?, attrs: AttributeSet?) : super(
            context!!, attrs
        )
    
        override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
            super.onSizeChanged(h, w, oldh, oldw)
        }
    
        @Synchronized
        override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
            super.onMeasure(heightMeasureSpec, widthMeasureSpec)
            setMeasuredDimension(measuredHeight, measuredWidth)
        }
    
        override fun onDraw(c: Canvas) {
            c.rotate(-90f)
            c.translate(-height.toFloat(), 0f)
            super.onDraw(c)
        }
    
        override fun onTouchEvent(event: MotionEvent): Boolean {
            if (!isEnabled) {
                return false
            }
            when (event.action) {
                MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE, MotionEvent.ACTION_UP -> {
                    progress = max - (max * event.y / height).toInt()
                    onSizeChanged(width, height, 0, 0)
                }
                MotionEvent.ACTION_CANCEL -> {}
            }
            return true
        }
    }

进度条的属性
示例文件

    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item android:id="@android:id/background">
            <shape>
                <corners android:radius="5dp" />
                <solid android:color="#90A4AE" />
            </shape>
        </item>
    
        <item android:id="@android:id/secondaryProgress">
            <clip>
                <shape>
                    <corners android:radius="5dp" />
                    <solid android:color="#FFEB3B" />
                </shape>
            </clip>
        </item>
    
        <item android:id="@android:id/progress">
            <clip>
                <shape>
                    <corners android:radius="5dp" />
                    <solid android:color="#2196F3" />
                </shape>
            </clip>
        </item>
    
    </layer-list>
​    <com.ikotliner.mylauncher.VerticalSeekBar
​        android:id="@+id/seek_bar"
​        android:layout_width="match_parent"
​        android:layout_height="200dp"
​        android:maxHeight="60dp"
​        android:minHeight="60dp"
​        android:progressDrawable="@drawable/seek_bar_background"/>

获取应用清单

    fun getAppList(context: Context): MutableList<ResolveInfo> {
        val pm = context.packageManager
        val mainIntent = Intent(Intent.ACTION_MAIN, null)
        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER)
        val activities = pm.queryIntentActivities(mainIntent, 0)
        return activities
    }

申请系统设置修改权限

    //Manifest文件添加权限
    <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
    
    //判断是否有修改系统设置的权限
    private fun requestPermission() {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
            if (!Settings.System.canWrite(this)) {
                val intent = Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
                intent.data = Uri.parse("package:$packageName");
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent)
            }
        }
    }

申请读取系统状态权限


    //Manifes文件中添加权限
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    //申请权限
    val checkCallPhonePermission =
        ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE)
    if (checkCallPhonePermission != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(
            context as Activity,
            arrayOf(Manifest.permission.READ_PHONE_STATE),
            0
        )
        return
    }

创建前台服务


    //新建通知
    private Notification createForegroundNotification() {
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    
        // 唯一的通知通道的id.
        String notificationChannelId = "notification_channel_id_01";
    
        // Android8.0以上的系统,新建消息通道
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //用户可见的通道名称
            String channelName = "Foreground Service Notification";
            //通道的重要程度
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel notificationChannel = new NotificationChannel(notificationChannelId, channelName, importance);
            notificationChannel.setDescription("Channel description");
            //LED灯
            notificationChannel.enableLights(true);
            notificationChannel.setLightColor(Color.RED);
            //震动
            notificationChannel.setVibrationPattern(new long[]{0, 1000, 500, 1000});
            notificationChannel.enableVibration(true);
            if (notificationManager != null) {
                notificationManager.createNotificationChannel(notificationChannel);
            }
        }
    
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, notificationChannelId);
        //通知小图标
        builder.setSmallIcon(R.drawable.ic_launcher_foreground);
        //通知标题
        builder.setContentTitle("ContentTitle");
        //通知内容
        builder.setContentText("ContentText");
        //设定通知显示的时间
        builder.setWhen(System.currentTimeMillis());
        //设定启动的内容
        Intent activityIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 1, activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        builder.setContentIntent(pendingIntent);
    
        //创建通知并返回
        return builder.build();
    
    //在onStartCommand()中开启前台服务
    // 获取服务通知
    Notification notification = createForegroundNotification();
    //将服务置于启动状态 ,NOTIFICATION_ID指的是创建的通知的ID
    startForeground(NOTIFICATION_ID, notification);

下载Android源代码

Ubuntu下载安卓源代码

Ubuntu查看端口被占用的进程

    sudo lsof -i:端口号
2

评论 (0)

取消